建立大根堆

          最近复习堆排序和优先队列,其中重要的一环是构建大根堆,即将无序的数组构建成堆有序,堆有序需区别于堆排序,堆有序仅仅是父节点总是不小于子节点,但并不是整个数组是排好了序的。

     构建的二叉树是完全二叉树,所以:

     1.当根节点索引为1:a.最后一个非叶子节点的索引为n/2;b.若父节点的索引是i,则它左孩子节点为2i,右孩子节点为2i+1

     2.当根节点索引为0:a.最后一个非叶子节点的索引为n/2-1;b.若父节点的索引是i,则它左孩子节点为2i+1,右孩子节点为2i+2

     

     自己总结的构建大根堆的思路:

     我们假设根节点的索引为1。

     1.从最后一个非叶子节点为父节点的子树出发,从右往左,从下往上进行调整操作(怎么调整下面讲)。这里需注意的是:

        a.是以该非叶子节点为父节点的子树,即该父节点以下的所有部分都进行调整操作。

        b.由于是从右往左从下往上,则某一步进行调整时在调整它之前的那些子树已经是堆有序了,即走到某个非叶子节点时,它的子树已经是堆有序了(因为是从下往上)

     2.即调整函数 :

        a.如果该节点比它的两个孩子节点都大,则该节点不用调整了,因为它的孩子节点也是堆有序 (上面b已说明)

        b.如果该节点比它的两个子节点中的较大节点小,即array[i]

void buildBigHeap(float array[], int len) {
	for (int i = len / 2; i > 0; i--) {         //从最大的非叶子节点开始(len/2),到根节点终止
		adjust(array, i, len);          
	}
}
void adjust(float array[], int i, int len) {
	float temp = array[i];                      //将该节点的值保存为temp
	for (int k = i * 2; k <= len;k=k*2) {       //k从2i出发,但循环里k要变成下一个开始的节点,所以k=2k
		if (k < len&&array[k] < array[k + 1]) {         //左右子节点都有值且左小于右
			k = k + 1;                  //k保存右节点的索引(右节点较大)
		}
		if (temp >= array[k]) {           
			break;
		}
		else {
			array[i] = array[k];         //父节点赋值为array[k]
			i = k;                       //i保存每轮迭代的父节点索引,跟父节点交换的那个节点索引为k,传给i,且停止的时候将temp传给array[i]
		}
	}
	array[i] = temp;
}

另外附上构建小根堆的python代码:(转自gensim word2vec(构建哈夫曼树时需要用到小根堆),它的根节点的索引为0,所以最后一个节点的索引为n-1,节点i的左孩子节点索引为2i-1,)

def cmp_lt(x, y):
    # Use __lt__ if available; otherwise, try __le__.
    # In Py3.x, only __lt__ will be called.
    return (x < y) if hasattr(x, '__lt__') else (not y <= x)
def _siftdown(heap, startpos, pos):
    newitem = heap[pos]
    # Follow the path to the root, moving parents down until finding a place
    # newitem fits.
    while pos > startpos:
        parentpos = (pos - 1) >> 1
        parent = heap[parentpos]
        if cmp_lt(newitem, parent):
            heap[pos] = parent
            pos = parentpos
            continue
        break
    heap[pos] = newitem
def _siftup(heap, pos):
    endpos = len(heap)
    startpos = pos
    newitem = heap[pos]
    # Bubble up the smaller child until hitting a leaf.
    childpos = 2*pos + 1    # leftmost child position
    while childpos < endpos:
        # Set childpos to index of smaller child.
        rightpos = childpos + 1
        if rightpos < endpos and not cmp_lt(heap[childpos], heap[rightpos]):
            childpos = rightpos
        # Move the smaller child up.
        heap[pos] = heap[childpos]
        pos = childpos
        childpos = 2*pos + 1
    # The leaf at pos is empty now.  Put newitem there, and bubble it up
    # to its final resting place (by sifting its parents down).
    heap[pos] = newitem
    _siftdown(heap, startpos, pos)
def heapify(x):
    """Transform list into a heap, in-place, in O(len(x)) time."""
    n = len(x)
    # Transform bottom-up.  The largest index there's any point to looking at
    # is the largest with a child index in-range, so must have 2*i + 1 < n,
    # or i < (n-1)/2.  If n is even = 2*j, this is (2*j-1)/2 = j-1/2 so
    # j-1 is the largest, which is n//2 - 1.  If n is odd = 2*j+1, this is
    # (2*j+1-1)/2 = j so j-1 is the largest, and that's again n//2-1.
    for i in reversed(xrange(n//2)):
        _siftup(x, i)

当进行堆排序的时候,如果从小到大排序,则需用到大顶堆,相反就用到小顶堆。它的步骤为:

1.先将无序二叉树变成大顶堆,即上面所讲。

2.将根节点与最后一个叶子结点交换,这时将最后一个叶子结点抛弃(其实只是暂时不管),这样现在这颗二叉树的最后一个叶子结点变成了之前的倒数第二个。

3.接下来将对根节点进行调整操作,注:只是根节点和子树进行调整,而不是像构建堆有序那样从下往上从右往左。接着跳至2,循环往复直到根节点。

4.最后将该二叉树层次遍历则是有序二叉树

 

 

  

 

       

你可能感兴趣的:(数据结构与算法,堆)