堆排序的概念

文章介绍了堆的基本概念,包括大根堆和小根堆的定义,并详细阐述了如何通过顺序存储的完全二叉树建立大根堆。接着,文章给出了建立大根堆和调整堆的代码实现,并解释了基于大根堆的堆排序过程,即每次将堆顶元素与末尾元素交换并重新调整堆,直到完成排序。


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

选择排序分为:

  • 简单选择排序
  • 堆排序

一、堆(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];
	}
}
### C++大根堆排序实现 堆排序是一种基于比较的选择排序方法,其核心思想是利用堆这种数据结构所设计的一种排序算法。堆分为大根堆和小根堆两种形式,在大根堆中,父节点的值总是大于等于子节点的值。 以下是C++实现的大根堆排序代码示例: ```cpp #include <iostream> using namespace std; // 调整堆的函数 void adjustHeap(int arr[], int i, int length) { int temp = arr[i]; // 取出当前元素i for (int k = 2 * i + 1; k < length; k = 2 * k + 1) { // 从i结点的左子结点开始 if (k + 1 < length && arr[k] < arr[k + 1]) { // 如果右子结点存在且较大,则指向右子结点 k++; } if (arr[k] > temp) { // 如果子结点大于父结点,则将较大的子结点赋值给父结点 arr[i] = arr[k]; i = k; } else { break; } } arr[i] = temp; // 将temp值放到最终的位置 } // 堆排序函数 void heapSort(int arr[], int length) { // 构建初始堆 for (int i = length / 2 - 1; i >= 0; i--) { adjustHeap(arr, i, length); } // 进行堆排序 for (int j = length - 1; j > 0; j--) { swap(arr[0], arr[j]); // 将当前最大值放到数组最后 adjustHeap(arr, 0, j); // 对剩余部分重新调整为堆 } } // 测试堆排序 int main() { int arr[] = {9, 8, 7, 6, 5, 4, 3, 2, 1}; int length = sizeof(arr) / sizeof(arr[0]); cout << "原始数组:" << endl; for (int i = 0; i < length; i++) { cout << arr[i] << " "; } cout << endl; heapSort(arr, length); cout << "排序后的数组:" << endl; for (int i = 0; i < length; i++) { cout << arr[i] << " "; } cout << endl; return 0; } ``` 上述代码实现了大根堆排序的核心功能[^3]。`adjustHeap` 函数用于维护堆的性质,而 `heapSort` 则完成了整个堆排序的过程。通过构建初始堆并将最大值逐步移至数组末端的方式完成排序过程。 #### 关键点解析 - **堆的构建**:从最后一个非叶子节点向上逐层调整,使得整个数组满足大根堆的特性。 - **交换与重建**:每次将堆顶的最大值与未排序序列中的最后一个元素互换位置,并对剩下的部分再次调用调整函数以恢复堆的性质。 此实现方式的时间复杂度为 O(nlogn),空间复杂度为 O(1)[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

快苏排序OAO

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

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

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

打赏作者

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

抵扣说明:

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

余额充值