时间:2019-03-20
本文章向大家介绍堆,主要包括堆使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1. 定义

由两条性质来定义。

  • 首先,它是一棵完全二叉树:从根节点起每一层从左到右填充。一棵高度为 dd 的完全二叉树除了第 d1d-1 层外,每一层都是满的;且底层节点集中在左边的若干位置上。

  • 其次,堆中存储的数据是局部有序的。即,节点存储的值与子节点存储的值之间存在某种关系。根据这种关系,堆可分为以下两种:

    • 最大堆:任意一个节点存储的值都大于等于其任意一个子节点存储的值。
    • 最小堆:任意一个节点存储的值都小于等于其任意一个子节点存储的值。

    无论最大堆还是最小堆,任何一个节点与其兄弟节点之间都没有必然联系!

2. 使用数组实现完全二叉树

  • 假设在完全二叉树中,逐层而下、从左到右,给每一个节点编号(从0开始)。
  • 把每一个节点按照其节点序号放在一个数组中。
  • 假设 nn 为节点的总数, rr 为节点序号,则有如下关系:
    • rr 的父节点序号:parent(r)=(r1)/2parent(r)=(r-1)/2,当 r0r\ne 0 时(取下整)
    • rr 的左子节点序号:leftChild(r)=2r+1leftChild(r)=2r+1,当 2r+1<n2r+1<n
    • rr 的右子节点序号:rightChild(r)=2r+2rightChild(r)=2r+2,当 2r+2<n2r+2<n

3. 建堆

  • 方法:自底向上构建堆。

  • 具体过程:从最后的内部节点开始,到根为止,该算法检查这些节点存储的值 KK 是否满足父母优势(即堆的第二条性质)。如果该节点不满足父母优势要求,算法把节点存储的值 KK 和它子女中存储的最大的值进行交换,然后再次检查在新位置上, KK 是否满足父母优势要求。这个过程一直持续到对 KK 的父母优势要求满足为止。
    对于以当前内部节点为根的子树,在完成它的“堆化”以后,算法对于该节点的直接前趋进行同样的操作。在对树的根完成这种操作以后,算法停止。


4. 堆排序

  • 思想:每次删除堆中最大的值,即从大到小依次取出堆中的元素。
  • 具体过程
    1. 的值和堆的最后一个节点的值 KK 做交换;
    2. 堆的规模减1;
    3. 再一次构建堆,即从根开始,严格按照我们自底向上构建堆的做法,把 KK 沿着树向下筛选,来对这颗较小的树进行“堆化”;
    4. 重复1~3步,直至取出所有的节点。

5. C++ 实现

// 为节点pos找到一个合适的容身之所,使以pos节点为根的二叉树满足堆的要求,前提是pos的左右子树皆已是堆
void shiftdown(int heap[], size_t n, size_t pos) {
    int node_cpy = heap[pos];
    size_t cur_pos = pos;

    size_t maximum = 2 * cur_pos + 1;	// 左子节点(如果存在的话),且设左子节点存储的值比右子节点存储的值大
    size_t right = maximum + 1;			// 右子节点(如果存在的话)

	// 左子节点存在,即当前节点还是内部节点
    while(maximum < n) {
    	// 右子节点也存在,此时需要找出左右子节点中较大者
        if(right < n) {
            maximum = heap[right] > heap[maximum] ? right : maximum;
        }
        // 小于左右子节点中的最大值
        if(node_cpy < heap[maximum]) {
            heap[cur_pos] = heap[maximum];
            cur_pos = maximum;

            maximum = 2 * cur_pos + 1;
            right = maximum + 1;
        }
        else {
            break;
        }
    }

    heap[cur_pos] = node_cpy;
}

// 建堆过程
void build_heap(int A[], size_t n) {
	// 从最后一个内部节点开始
    for(int i=n/2-1; i>=0; --i) {
        shiftdown(A, n, i);
    }
}

// 堆排序
void heap_sort(int A[], size_t n) {
    build_heap(A, n);

    size_t size = n;
    for(size_t i=0; i<n; ++i) {
        shiftdown(A, size, 0);
		std::swap(A[0], A[--size]);
    }
}