定义
堆(heap)是一类特殊的数据结构的统称。
堆通常是一个可以被看做一棵完全二叉树的数组对象。
About Binary Trees(二叉树相关)
Binary Tree(二叉树): 是树的一种,主要的特点是二叉树的所有节点最多只有两个叶节点。除此之外没有别的要求。
Complete Binary Tree(完全二叉树): 二叉树的一种。在完全二叉树当中,除了最后一层之外,所有层的节点都是满的,且最后一层的节点也是从左到右的。优先填满左边的节点。
Full Binary Tree(满二叉树): 二叉树的一种。满二叉树的所有层,包括最后一层,都是满的。也就是说,除了最后一层的节点外所有的节点都有两个子节点。
About Binary Heap(二叉堆相关)
Heap(堆): 堆是一种树。在树的性质之外,堆要求节点按照大小(父节点比子节点大/父节点比子节点小)来排列。
Binary Heap(二叉堆): 二叉堆是近似于完全二叉树的一种堆。除完全二叉树的性质外,二叉堆还要求堆内元素按照大小排列。
Min Heap(最小堆): 最小的键值总是在最前面。换句话说,所有的父节点都比他们的子节点小。
Max Heap(最大堆): 最大的键值总是在最前面。换句话说,所有的父节点都比他们的子节点大。
作者:匿名用户
链接:https://www.zhihu.com/question/36134980/answer/1202061173
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
思想
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。
堆的性质保证了其根元素为最大或最小值;
每一次创建大/小顶堆,在堆顶取出最大/小值,把当前剩余的最后一个元素放到堆顶;
再次建立大/小顶堆,得到全部数据中第二大/小的值,取出,把当前剩余的最后一个元素放到堆顶;
重复上述步骤,直到全部元素都被取出。
假设一个小根堆的左、右子树都已是堆,并且根的元素名为root ,其左右子节点为left 和right ,这种情况下,有两种可能:
(1) root<=left 并且 root<=right ,此时堆已完成;
(2)root>left 或者 root>right,此时 root 应与两个子女中值较小的一个交换,结果得到一个堆,除非 root 仍然大于其新子女的一个或全部的两个。这种情况下,我们只需简单地继续这种将 “拉下来”的过程,直至到达某一个层使它小于它的子女,或者它成了叶结点。
步骤
以升序为例,需要不断创建大顶堆
- 初始化大顶堆 (从倒数第二行的节点开始,从下往上、从右往左,这样遍历的顺序保证了一个小根堆的左、右子树都已是堆)
假设一个小根堆的左、右子树都已是堆,并且根的元素名为root ,其左右子节点为left 和right ,这种情况下,有两种可能:
(1) root<=left 并且 root<=right ,此时堆已完成;
(2)root>left 或者 root>right,此时 root 应与两个子女中值较小的一个交换,结果得到一个堆,除非 root 仍然大于其新子女的一个或全部的两个。这种情况下,我们只需简单地继续这种将 “拉下来”的过程,直至到达某一个层使它小于它的子女,或者它成了叶结点。 - 将数组第一个元素(即根节点)与数组最后一个元素交换(这时最后一个元素成为了最大的)
- 重塑大顶堆
- 重复2.3.步骤,直至只剩下根节点
代码实现
- Down——把i逐层向下比较,放到合适的地方;结合一定的调用前提,实现了把以i为堆顶的子堆变为大顶堆的功能。
void Down(int array[], int i, int size)
{
int parent = i;
int child = 2 * i + 1;
while (child < size)
{
//比较两个儿子的大小(如果有两个儿子的前提下)
//换成更大的那个去和父亲比较
if (child + 1 < size && array[child] < array[child + 1])
{
child++;
}
//如果父亲不比两个儿子都大
//那么交换父亲和儿子
//并且继续往下查看以刚刚的儿子为根的子堆
if (array[parent] < array[child])
{
swap(array, parent, child);
parent = child;
child = 2 * child + 1;
}
//如果父亲比两个儿子都大,则大顶堆已经完成
//因为我们遍历的顺序保证了一个子堆的左右子树都已经是大顶堆
else break;
}
}
- BuildHeap——将原无序数组初始化为一个大顶堆
void BuildHeap(int array[], int size)
{
for (int i = size / 2 - 1; i >= 0; i--)
Down(array, i, size);
}
比如,按照顺序,先对倒数第二排的节点调用Down函数,结果是,倒数第二排结合最后一排成为了一个个大顶堆;
然后循环来到倒数第三排的节点,再调用Down(由上述知,已经满足了每个子堆的左右子树都已经是大顶堆的前提),之后倒数第三、二、一排共同组成了一个个大顶堆;
以此类推,最终构建出了一个真正的大顶堆。
- HeapSort——实现堆排序
void HeapSort(int array[], int size)
{
//初始化一个大顶堆
BuildHeap(array, size);
//将堆顶元素和堆中剩余的最后一个元素交换
//再把交换后的堆顶放置到合适的地方
//由于只改变了堆顶的元素,不会改变其他子堆已经是大顶堆的事实
for (int i = size - 1; i > 0; i--)
{
swap(array, 0, i);
Down(array, 0,i);
}
}
- main
int main()
{
int array[] = { 20, 19, 18, 30, 13, 30, 27, 15, 10 };
int size = sizeof(array) / sizeof(int);
HeapSort(array, size);
for (int i = 0; i < size; i++)
cout << array[i] << ' ';
}
输出结果