堆排序 - 数据结构 - 算法


超全经典排序大汇总


算法思想

每一趟将堆顶元素加入到有序子序列(与待排元序列中的最后一个元素交换),并将待排序列再次调整为大根堆(即将小元素 “下坠” )。

注:

  1. 基于 “大根堆” 的堆排序得到的是递增序列
  2. 一个结点每“下坠”一层,最多只需要比对关键字两次
  3. 若树高为h,某结点在第i层,将这个结点向下调整最多只需要“下坠”h - i层,关键字的比对次数不超过2(h - i)
  4. n个结点的完全二叉树,树高 h = ⌊ l o g 2 n ⌋ + 1 h = \lfloor log{_2}n \rfloor + 1 h=log2n+1

时间复杂度

  1. 最好 — O ( n l o g n ) O(nlogn) Onlogn
  2. 最坏 — O ( n l o g n ) O(nlogn) O(nlogn)
  3. 平均 — O ( n l o g n ) O(nlogn) Onlogn

注:时间复杂度 = O(n) + O(nlog_2n) = O(nlogn)

演示动画

堆.gif

空间复杂度

O ( 1 ) O(1) O1

稳定性

不稳定

适用性

仅适用于顺序表

算法特点

  1. 不稳定排序
  2. 只能用于顺序结构,不能用于链式结构
  3. 初始建堆所需要的比较次数比较多。因此,当记录数较少时,不宜采用。当记录较多时,较为高效。

核心代码

1. 建大根堆思路

把所有非终端结点都检查一遍,是否满足大根堆的要求,如果不满足,则进行调整:

  1. 检查当前结点是否满足根 >= 左、右,如果不满足,则将当前结点与更大的一个孩子交换
  2. 若元素互换破坏了下一级的堆,则采用相同的方式继续往下调整(小元素不断“下坠”)
//将以k为根结点的树调整为大根堆 
void HeadAdjust(int a[], int k, int len){//注:除k结点外其他已经有序 
	a[0] = a[k];//a[0]暂存子树根节点 
	for(int i = 2 * k; i <= len; i *= 2){//沿较大子结点筛选 
		if(i < len && a[i] < a[i + 1]) i ++;//i为较大子结点下标 (i<len表示k有右孩子) 
		if(a[0] >= a[i]) break;//筛选结束 
		else{
			a[k] = a[i];//递归处理子结点 
			k = i;
		}
	}
	a[k] = a[0];
}
//建大根堆 时间复杂度---O(n)  
void BuildHeap(int a[], int len){//从下往上建堆,从最后一个叶子结点的根节点开始 
	for(int i = len / 2; i > 0; i --){//处理所有非终端结点 
		HeadAdjust(a, i , len);
	}
}//建堆过程关键字的比较次数不超过4n(定理) 
2. 堆排序思路

每一趟将堆顶元素加入到有序子序列(与待排列中的最后一个元素交换),并将待排序列再次调整为大根堆(小元素不断“下坠”)

//堆排序的完整逻辑 
void HeapSort(int a[], int len){
	BuildHeap(a, len);//建堆 O(n) 
	for(int i = len; i > 1; i --){//从后往前处理 ,共n-1趟交换和调整 
		swap(a[i], a[1]);//将堆顶元素(最大元素)与堆底元素交换 
		HeadAdjust(a, 1, i - 1);//把剩余待排元素调整为堆 
	}
}//每趟时间复杂度不超过o(h) =  O(logn),共 n-1趟 

完整代码

#include <iostream> 
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>

using namespace std;
const int N = 20;

int num;
int data[N],idx;

//将以k为根结点的树调整为大根堆 
void HeadAdjust(int a[], int k, int len){//注:除k结点外其他已经有序 
	a[0] = a[k];//a[0]暂存子树根节点 
	for(int i = 2 * k; i <= len; i *= 2){//沿较大子结点筛选 
		if(i < len && a[i] < a[i + 1]) i ++;//i为较大子结点下标 (i<len表示k有右孩子) 
		if(a[0] >= a[i]) break;//筛选结束 
		else{
			a[k] = a[i];//递归处理子结点 
			k = i;
		}
	}
	a[k] = a[0];
}

//建大根堆 时间复杂度---O(n)  
void BuildHeap(int a[], int len){//从下往上建堆,从最后一个叶子结点的根节点开始 
	for(int i = len / 2; i > 0; i --){//处理所有非终端结点 
		HeadAdjust(a, i , len);
	}
}//建堆过程关键字的比较次数不超过4n(定理) 

//堆排序的完整逻辑 
void HeapSort(int a[], int len){
	BuildHeap(a, len);//建堆 O(n) 
	for(int i = len; i > 1; i --){//从后往前处理 ,共n-1趟交换和调整 
		swap(a[i], a[1]);//将堆顶元素(最大元素)与堆底元素交换 
		HeadAdjust(a, 1, i - 1);//把剩余待排元素调整为堆 
	}
}//每趟时间复杂度不超过o(h) =  O(logn),共 n-1趟 

int main(){
	//打开文件 
	ifstream infile;
	infile.open("D:\\学习\\数据结构\\第8章排序\\in.txt", ios::in);
	
	//写文件 
	ofstream outfile;
	outfile.open("D:\\学习\\数据结构\\第8章排序\\out.txt", ios::out);
	
	if(!infile.is_open()){//判断文件打开是否成功 
		cout << "file open failure!" << endl;
	}
	
	infile >> num;//读取元素个数 
	while(num --){//将文件中的元素复制到data[1...n] 数组中
		infile >> data[++ idx];
	}
	
	cout << "排序前元素序列:" << endl;
	for(int i = 1; i <= idx; i ++) cout << data[i] << ' '; cout << endl;
	
	cout << "使用sort函数排序后序列: " << endl;
	sort(data + 1, data + 1 + idx); 
	for(int i = 1; i <= idx; i ++) cout << data[i] << ' '; cout << endl;
	
	HeapSort(data, idx);
	cout << "使用堆排序后序列为:" << endl;
	for(int i = 1; i <= idx; i ++) cout << data[i] << ' '; cout << endl;
	
	num = idx, idx = 0, outfile << num << endl;//写入数据数num以及在行末写入\n 
	while(num --){
		outfile << data[++ idx] << ' ';//将排序后的数据写到文件中 
	}
	outfile << endl;//向文件末尾写入\n结束符 
	
	//关闭文件 
	infile.close();
	outfile.close();
	return 0;
}

输入数据(in.txt)

10
13 69 86 99 59 23 64 32 86 72

输出数据(out.txt)

10
13 23 32 59 64 69 72 86 86 99 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

soyisou

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

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

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

打赏作者

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

抵扣说明:

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

余额充值