上次说了个Huffman编码,这次说说堆排序,先贴Python代码:
class MaxHeap: arrInfo = [] heapSize = 0 def __init__(self): self.initArr() def initArr(self): self.arrInfo = [0]+[4,12,32,54,2,54,34,76,34,87] self.heapSize = len(self.arrInfo)-1 def maxHeapify(self,index): l = self.leftIndex(index) r = self.rightIndex(index) largest = index if l <= self.heapSize and self.arrInfo[l] > self.arrInfo[index]: largest = l if r <= self.heapSize and self.arrInfo[r] > self.arrInfo[largest]: largest = r if largest != index: ln = self.arrInfo[largest] self.arrInfo[largest] = self.arrInfo[index] self.arrInfo[index] = ln self.maxHeapify(largest) def buildMaxHeap(self): for i in range(self.heapSize/2,0,-1): self.maxHeapify(i) def heapSort(self): self.buildMaxHeap() for i in range(self.heapSize,1,-1): a1 = self.arrInfo[1] self.arrInfo[1] = self.arrInfo[i] self.arrInfo[i] = a1 self.heapSize -= 1 self.maxHeapify(1) def leftIndex(self,index): return index*2 def rightIndex(self,index): return index*2+1 def printResult(self): print self.arrInfo[1:] self.heapSort() print self.arrInfo[1:] MaxHeap().printResult()
第一步初始化数组,有点特别的地方,就是不包含第一个元素,因为数组的索引是从0开始,而排序时,堆的根节点编号应该置为1,这样好计算指定节点的左右孩子(其实从0编号,也可以算,但是繁琐一点),如果编号为0,那么根据上述简单的计算方法(该计算方法是比较符合二叉树的特性的),可以得出左右孩子的编号分别为:0和1,这显然是不对的,但是如果根节点编号为1,计算结果为:2和3,非常正确。
第二步提供一个maxHeapify方法,该方法也比较简单,如果发现子节点比父节点的值大,则进行交换,再递归执行下去。如若对某个节点(存在子节点)调用该方法,不能保证该节点的值一定大于左右节点的值(这个要注意,不然就曲解了该方法的意义了),但是一定可以实现该方法递归调用的最后一次的那个父节点的值大于左右节点的值。所以该方法的调用,需要使用一种自下而上的调用,且看第三步。
第三步就要构造一个最大堆(父节点的值一定都大于子节点的值,但不是排好序的),buildMaxHeap自下而上的调用maxHeapify方法。最上层当然是根了,这里的最下层是最后一个包含子节点的节点(根据二叉树的特性,该节点的编号就应该是length/2,length为堆的大小,也就是二叉树的总节点个数,这里Python会自动只取整数部分)。那么自下而上调用,就是从最下面的那个节点到最上面的根节点,依次调用maxHeapify方法,这样一个最大堆就构造好了,保证了所有的父节点的值一定是大于子节点的了。
第四步就是排序了。这步比较有意思,基于上面的最大堆,根节点元素是堆中最大的。将根节点与最后一个节点交换,然后去除最后一个节点(堆的大小就减一,并且该节点是堆中最大的),对根节点(新的根节点)再调用maxHeapify方法(这里调用完后会保证所有的父节点的值一定大于子节点的值,所以根节点就是堆中最大的值,这里要好好悟),递归下去,排序就完成了。
堆排序就啰嗦完了,美中不足的就是缺少图文并茂的描述,太多的文字也许会看的迷糊,但是原生态的,没那么多精力去整图了,