建立大根堆

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

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

     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]<max(array[2i],array[2i+1]),将  array[i]赋给temp,以后每次都跟temp比较,好多博客说的是交换两个值,其实程序里是直接比较temp。将max(array[2i],array[2i+1])赋给array[i]。接着从max(array[2i],array[2i+1]) 对应的那个节点出发,继续进行该操作,直到该节点到达了n。当然每次判断边界条件为左子树的索引小于n,则右子树才有值。

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.将根节点与最后一个叶子结点交换,这时将最后一个叶子结点抛弃(其实只是暂时不管,进行后面的调整操作时就不用考虑抛弃的节点,但最终第4步层次遍历的时候仍然需要遍历这些节点),这样现在这颗二叉树的最后一个叶子结点变成了之前的倒数第二个。

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

4.最后将该二叉树层次遍历(这里是遍历含抛弃节点的二叉树)则是有序二叉树

 

 

  

 

       

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值