View Single Post
Old Sep 13th, 2006, 12:30 PM   #8
DaWei
Resident Grouch
 
DaWei's Avatar
 
Join Date: Jun 2005
Posts: 6,453
Rep Power: 10 DaWei is on a distinguished road
This is a heapsort. Rather than just pluck a function from the library and inexplicably illustrate the sort, I have implemented the sort. This approach uses an array representation for the tree. There are interesting things about the heapsort which aren't illustrated here, such as sorting in-place and the implications that has on the min/max nature of the heap. Google will be more than happy to satisfy your curiosity.

A heap may be either a min heap or a max heap. The requirements for a heap are that every parent is less (greater) than its children. The tree must also be complete; that is, every level is filled with the possible exception of the last, which is packed to the "left". The sort is accomplished by plucking out the root and placing it in the output list. It is then replaced by the most extreme element, which almost certainly breaks the heapness. The tree is then re-heaped and it's all done again. Obviously, the tree shrinks as the sort progresses. This storage may be used to accomplish the (quasi) in-place sort mentioned above. In re-heaping, one finds that only half the tree needs to be adjusted. Moving into this half, one finds that only half of the sub-hive needs to be adjusted. This is the "divide and conquer" approach that also makes sorts like quicksort effective.

As for time complexity, forming a heap from a wild tree requires, worst case, 3N comparisons. The sort, which requires re-heaping (quicker than the heapify), needs, worst case, Nlog(N) comparisons.

class Heap:
    
    def __init__ (I, theHeap = [], desc = False):
        '''
        Initializes a newly instantiated heap.  If data is passed
        in the tree will be heapified on construction.
        '''
        I.theHeap = theHeap
        I.nGoodies = len (theHeap)
        I.desc = desc
        if I.nGoodies > 0: I.heapify ()
        
    def theWinnah (I, a, b):
        '''
        Comparison function that adapts to whether or not the sort
        is ascending or descending.
        '''
        if I.desc == False: return (a < b)
        else: return (b <= a)
        
    def showHive (I):
        '''
        Arrange the tree in a visual hive.  The tree must have sixteen
        or fewer members.
        '''
        if I.nGoodies > 16: 
            print "Hive too large to show"
            return
        i = 0
        level = 0
        lineLength = 80
        while i < I.nGoodies:
            pcount = 2**level
            cellWidth = lineLength / pcount
            line = ""
            while pcount > 0:
                word = I.theHeap [i]
                line += word.center (cellWidth, ' ')
                pcount -= 1
                i += 1
                if i >= I.nGoodies: break
            print line
            level += 1
    
    def heapify (I, iParent = 0):
        '''
        This adjusts the tree so that it qualifies as a heap.  That is,
        it is a complete tree and any parent is smaller (larger, for
        descending sorts) than either of its children.  This is not as
        efficient as the reheap process, because the entire tree must
        be examined.
        '''
        if I.iLeft (iParent) < I.nGoodies:
            # Left child exists
            if I.iLeft (I.iLeft (iParent)) < I.nGoodies:
                # And has a child
                I.heapify (I.iLeft (iParent))
            if I.theWinnah (I.theHeap [I.iLeft (iParent)].lower (),
                            I.theHeap [iParent].lower ()):
                # Left child less (greater) than parent, 
                # swap and reaheapify the hive
                I.swap (iParent, I.iLeft (iParent))
                I.heapify (I.iLeft (iParent))
                #I.heapify (iParent)
        if I.iRight (iParent) < I.nGoodies:
            # Right child exists
            if I.iRight (I.iRight (iParent)) < I.nGoodies:
                # And has a child
                #print I.theHeap [I.iRight (iParent)], I.iRight (iParent)
                I.heapify (I.iRight (iParent))
            if I.theWinnah (I.theHeap [I.iRight (iParent)].lower (), 
                            I.theHeap [iParent].lower ()):
                # Right child less (greater) than parent
                I.swap (iParent, I.iRight (iParent))
                I.heapify (I.iRight (iParent))
                #I.heapify (iParent)
    
    def reHeap (I, iParent = 0):
        '''
        Reheaps the tree after the root is removed and replaced by the
        most extreme (positionally) value.  Beginning at the root, only
        one of the child hives needs to be adjusted.  When the child hive
        is adjusted, only one of its child hives has to be adjusted.  This
        divide and conquer approach is where the efficiency arises.
        '''
        if I.iLeft (iParent) < I.nGoodies:
            # Left child exists
            if I.theWinnah (I.theHeap [I.iLeft (iParent)].lower (), 
                            I.theHeap [iParent].lower ()):
                # Not a heap, left child greater (smaller) than parent
                if I.iRight (iParent) < I.nGoodies \
                   and\
                   I.theWinnah (I.theHeap [I.iRight (iParent)].lower (), 
                                I.theHeap [I.iLeft (iParent)].lower ()):
                    # Right child lesser (greater) of the three
                    I.swap (iParent, I.iRight (iParent))
                    I.reHeap (I.iRight (iParent))
                else:
                    # Left child lesser (greater) of the three
                    I.swap (iParent, I.iLeft (iParent))
                    I.reHeap (I.iLeft (iParent))
            elif I.iRight (iParent) < I.nGoodies \
                 and\
                 I.theWinnah (I.theHeap [I.iRight (iParent)].lower (), 
                              I.theHeap [iParent].lower ()):
                # Not a heap, right child less (greater) than parent
                I.swap (iParent, I.iRight (iParent))
                I.reHeap (I.iRight (iParent))
                
    def iLeft (I, iParent): return 2 * iParent + 1
    
    def iRight (I, iParent): return 2 * iParent + 2
        
    def swap (I, srcIndex, dstIndex):
        '''
        Exchanges the two values at the specified indexes.
        '''
        temp = I.theHeap [srcIndex]
        I.theHeap [srcIndex] = I.theHeap [dstIndex]
        I.theHeap [dstIndex] = temp
    
    def sort (I):
        '''
        Sorts the heap by removing the root (the most extreme value)
        to the output list and replacing the root with the value at
        the most extreme position.
        '''
        
        unsorted = Heap (I.theHeap [:], I.desc)
        sorted = []
        while unsorted.nGoodies > 0:
            sorted.append (unsorted.theHeap [0])
            unsorted.nGoodies -= 1
            unsorted.theHeap [0] = unsorted.theHeap [unsorted.nGoodies]
            unsorted.reHeap ()
        I.theHeap = sorted [:]
        

def main():
    ''' Illustrate a heapsort.  If a list is passed in,
        it is heapified on construction.'''
    
    theThangs = ["This", "is", "an", "illustration",
                 "of", "a", "heapsort. ",
                 "Its", "time", "complexity", "is", 
                 "usually", "expressed", "as", "O(blah)"]
    
    print "The stuff: ",
    for i in theThangs: print i,
    print
    heap = Heap (theThangs)
    print "\nHeapified on construction: "
    heap.showHive ()
    heap.sort ()
    print
    print "Sorted (ascending): ",
    for i in heap.theHeap: print i,
    print
    print
    print "Set mode to descending"
    heap.desc = True;
    heap.heapify ()
    print "Heapified: "
    heap.showHive ()
    heap.sort ()
    print
    print "Sorted (descending): ",
    for i in heap.theHeap: print i,
    print
    raw_input ("\nPress ENTER to exit: ")

if __name__ == '__main__':
    main()
Quote:
Originally Posted by Output
The stuff:  This is an illustration of a heapsort.  Its time complexity is usually ex
pressed as O(blah)

Heapified on construction:
                                       a
              illustration                                 an
        Its                  is              complexity              as
   This      time       of        is     usually  expressed heapsort.  O(blah)

Sorted (ascending):  a an as complexity expressed heapsort.  illustration is is Its O
(blah) of This time usually

Set mode to descending
Heapified:
                                    usually
                  Its                                     time
     complexity              is               O(blah)               This
    a         an    expressed     is        as    heapsort. illustration    of

Sorted (descending):  usually time This of O(blah) Its is is illustration heapsort.
expressed complexity as an a

Press ENTER to exit:
__________________
Abstraction doesn't make it impossible to write bad code; it makes it possible to write superior code.
Contributor's Corner: Grumpy on C++ Exceptions DaWei on Pointers
DaWei is offline   Reply With Quote