【排序算法】堆排序初探

定义

堆(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 仍然大于其新子女的一个或全部的两个。这种情况下,我们只需简单地继续这种将 “拉下来”的过程,直至到达某一个层使它小于它的子女,或者它成了叶结点。

步骤

以升序为例,需要不断创建大顶堆

  1. 初始化大顶堆 (从倒数第二行的节点开始,从下往上、从右往左,这样遍历的顺序保证了一个小根堆的左、右子树都已是堆)
    假设一个小根堆的左、右子树都已是堆,并且根的元素名为root ,其左右子节点为left 和right ,这种情况下,有两种可能:
    (1) root<=left 并且 root<=right ,此时堆已完成;
    (2)root>left 或者 root>right,此时 root 应与两个子女中值较小的一个交换,结果得到一个堆,除非 root 仍然大于其新子女的一个或全部的两个。这种情况下,我们只需简单地继续这种将 “拉下来”的过程,直至到达某一个层使它小于它的子女,或者它成了叶结点。
  2. 将数组第一个元素(即根节点)与数组最后一个元素交换(这时最后一个元素成为了最大的)
  3. 重塑大顶堆
  4. 重复2.3.步骤,直至只剩下根节点

代码实现

  1. 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;
	}
}
  1. BuildHeap——将原无序数组初始化为一个大顶堆
void BuildHeap(int array[], int size)
{
	for (int i = size / 2 - 1; i >= 0; i--)
		Down(array, i, size);
}

比如,按照顺序,先对倒数第二排的节点调用Down函数,结果是,倒数第二排结合最后一排成为了一个个大顶堆;
然后循环来到倒数第三排的节点,再调用Down(由上述知,已经满足了每个子堆的左右子树都已经是大顶堆的前提),之后倒数第三、二、一排共同组成了一个个大顶堆;
以此类推,最终构建出了一个真正的大顶堆。

  1. 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);
	}
}
  1. 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] << ' ';
}

输出结果

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值