ASPN ActiveState Programmer Network  
ActiveState, a division of Sophos
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups
Submit Recipe
My Recipes

All Recipes
All Cookbooks


View by Category

Title: Two-pass Pairing Heap with Multipass Auxiliary List
Submitter: Raymond Hettinger (other recipes)
Last Updated: 2004/11/26
Version no: 1.3
Category: Algorithms

 

5 stars 1 vote(s)


Description:

Pairing heap refinement for fewer comparisons.

Source: Text Source

""" Two-pass pairing heap with multipass auxiliary list

This recipe adds a multipass auxiliary list to Tim Peter's code
for a two-pass pairing heap presented at:

    http://mail.python.org/pipermail/python-dev/2002-August/027531.html

The refinement produces a more balanced initial heap when fed with
random data.  This results in fewer comparisons than the basic
two-pass heap.  Details, analysis, and diagrams can be found at:

    http://www.cise.ufl.edu/~sahni/dsaaj/enrich/c13/pairing.htm

For random data, this implementation still makes more comparisons than
a sort using heapq or the builtin sort.  For partially ordered data, it
can perform better than the builtin sort.  Because of the list of lists
data structure, it always takes more memory than either heapq or the
builtin sort.

"""

from collections import deque

def _link(x, y):
    if x[0] <= y[0]:
        x.append(y)
        return x
    else:
        y.append(x)
        return y

def _merge(x):
    n = len(x)
    if n == 1:
        return []
    pairs = [_link(x[i], x[i+1]) for i in xrange(1, n-1, 2)]
    if n & 1 == 0:
        pairs.append(x[-1])
    pairs.reverse()
    x = pairs.pop()
    for i in pairs:
        x = _link(i, x)
    return x

class AuxList(deque):

    def multipass(self):
        while len(self) > 1:
            self.appendleft(_link(self.pop(), self.pop()))
        return self.pop()

class Heap:

    def __init__(self, iterable=[]):
        self.x = []
        self.aux = AuxList([[value] for value in iterable])

    def __nonzero__(self):
        return bool(self.x)

    def push(self, value):
        self.aux.append([value])

    def pop(self):
        if self.aux:
            self.x += self.aux.multipass()
        result = self.x[0]  # raises IndexError if empty
        self.x = _merge(self.x)
        return result

    def __getitem__(self, i):
        'Hack to make sorting as easy as:  list(Heap(input))'
        return self.pop()    

## Example call
>>> print list(Heap('abracadabra'))
['a', 'a', 'a', 'a', 'a', 'b', 'b', 'c', 'd', 'r', 'r']

Discussion:

For versions before Py2.4, subclass from list instead of collections.deque:

class AuxList(list):

    def multipass(self):
        while len(self) > 1:
            self[0:0] = [_link(self.pop(), self.pop())]
        return self.pop()



Add comment

Number of comments: 1

[0:0] vs .insert(0,, Christos Georgiou, 2006/05/10
Any special reason to prefer lst[0:0]= [obj] over lst.insert(0, obj)? I am asking out of curiosity.
Add comment



Highest rated recipes:

1. A simple XML-RPC server

2. Web service accessible ...

3. Wrapping template engine ...

4. Assignment in expression

5. SOLVING THE METACLASS ...

6. Povray for python

7. Calling Windows API ...

8. Generic filter logic ...

9. Function Decorators by ...

10. MS SQL Server log monitor




Privacy Policy | Email Opt-out | Feedback | Syndication
© 2006 ActiveState Software Inc. All rights reserved.