堆排序的概念


选择排序:在每一趟待排元素中选取关键字最小(或最大)的元素加入有序子序列

选择排序分为:

  • 简单选择排序
  • 堆排序

一、堆(Heap)的基本概念

若n个关键字序列L[1…n]满足下面某一条性质,则称为堆(Heap):

  • 若满足:L(i)≥L(2i)且L(i)≥L(2i+1)(1≤i≤n/2)–大根堆
  • 若满足:L(i)≤L(2i)且L(i)≤L(2i+1)(1≤i≤n/2) --小根堆

1.1 引入二叉树的顺序存储

在这里插入图片描述
有此可见,堆逻辑上可理解为一个顺序存储的完全二叉树

  • 大根堆–根>左,右的完全二叉树
  • 小根堆–根<左,右的完全二叉树

二、建立大根堆

基本思路:把所有分支结点检查一遍,判断是否满足大根堆的要求,如果不满足,则进行调整。

根据完全二叉树的性质

  • 2i<n
  • 2i+1<n
  • 叶子结点:i>n/2
  • 分支结点: i ≤ n / 2 i\leq n/2 in/2

故只需比较分支结点与其孩子结点的大小。找到最靠后的分支结点开始检查,如果不满足大根堆的条件,从左右孩子结点中挑较大的一个与根结点交换。接着不断从下往上调整(数组从后往前遍历)。


举例帮助理解:
在这里插入图片描述
i=8;只需检查 i ≤ 4 i\leq 4 i4的结点即可,即为上图圈出来的结点。
二叉树从下往上调整,故从09开始,接着检查78,然后是17,最后是53。
数组对应从后往前检查,i=4向前遍历


代码实现

建立大根堆:

void buildMaxHead(int arr[], int length) {
	for (int i = length / 2; i >= 1; i--) {//从后往前遍历数组
		HeadAdjust(arr, i, length);
	}
}

将以k为根的子树调整为大根堆:

void HeadAdjust(int arr[], int k, int len) {
	arr[0] = arr[k];//key
	for (int i = 2 * k; i <= len; i*=2) {//延key不断向下检查子节点
		if (arr[i] < arr[i + 1] && i + 1 <= len) { //将i指向较大子孩子
			i++;
		}
		if (arr[0] >= arr[i])//当满足大根堆,退出
			break;
		else {
			arr[k] = arr[i];
			k = i;//修改k值,继续向下检查
		}
	}
	arr[k] = arr[0];//将key放入最后检查的位置
}

三、基于大根堆进行排序

堆排序:每一趟将堆顶元素加入有序子序列(与待排序序列中的最后一个元素交换)

并将待排序元素序列再次调整为大根堆(小元素不断“下坠”)

举例帮助理解
在这里插入图片描述
此时已经完成一次堆排序,堆顶元素为最大元素
在这里插入图片描述
堆顶元素队列末尾元素互换。接着不断进行HeadAdjust(arr,1,len-1)


完整代码实现

#include<iostream>
using namespace std;
int N;
int arr[100050];
void HeadAdjust(int arr[], int k, int len) {
	arr[0] = arr[k];
	for (int i = 2 * k; i <= len; i *= 2) {
		if (arr[i] < arr[i + 1] && i + 1 <= len) { //将i指向较大子孩子
			i++;
		}
		if (arr[0] > arr[i])
			break;
		else {
			arr[k] = arr[i];
			k = i;
		}
	}
	arr[k] = arr[0];
}
void buildMaxHead(int arr[], int length) {
	for (int i = length / 2; i >= 1; i--) {
		HeadAdjust(arr, i, length);
	}
}

int main() {
	cin >> N;
	for (int i = 1; i <= N; i++) {
		cin >> arr[i];
	}
	buildMaxHead(arr, N);//建立大根堆
	//基于大根堆的堆排序
	for (int i = N; i >= 1;) {
		int t = arr[i];
		arr[i] = arr[1];
		arr[1] = t;
		HeadAdjust(arr,1,--i);
	}
	for (int i = 1; i <= N; i++) {
		if (i != 1)
			cout << " ";
		cout << arr[i];
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快苏排序OAO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值