排序算法之 堆排序

24 篇文章 0 订阅
12 篇文章 0 订阅

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树

什么是完全二叉树呢?可以看下这篇文章:https://blog.csdn.net/hanzhen7541/article/details/99437854

所以说,堆排序是将数组当做完全二叉树、根据完全二叉树的特性来进行排序的一种算法。利用最大堆的性质,堆的根节点一定大于子节点的值。具体的步骤可以看如下的gif:

具体的过程如下:

  • 首先,将所有的数字存储在堆中

  • 按大顶堆构建堆,其中大顶堆的一个特性是数据将被从大到小取出,将取出的数字按照相反的顺序进行排列,数字就完成了排序

  • 在这里数字 5 先入堆

  • 数字 2 入堆

  • 数字 7 入堆, 7 此时是最后一个节点,与最后一个非叶子节点(也就是数字 5 )进行比较,由于 7 大于 5 ,所以 7 和 5 交互

  • 按照上述的操作将所有数字入堆,然后从左到右,从上到下进行调整,构造出大顶堆

  • 入堆完成之后,将堆顶元素取出,将末尾元素置于堆顶,重新调整结构,使其满足堆定义

  • 堆顶元素数字 7 取出,末尾元素数字 4 置于堆顶,为了维护好大顶堆的定义,最后一个非叶子节点数字 5 与 4 比较,而后交换两个数字的位置

  • 反复执行调整+交换步骤,直到整个序列有序

代码实现如下(C++):

#ifndef HEAP_H_
#define HEAP_H_

#include <algorithm>
using namespace std;

class Heap_sort
{
public:
	Heap_sort();
	~Heap_sort();

	void heap_sort(int array[], int len);
	void buildMaxHeap(int array[], int len);//建堆
	void heapify(int array[], int i, int len);//调整堆

};

Heap_sort::Heap_sort()
{
}

Heap_sort::~Heap_sort()
{
}

void Heap_sort::heap_sort(int array[], int len){
	buildMaxHeap(array, len);

	for (int i = len - 1; i > 0; i--){
		swap(array[0], array[i]);//堆顶是最大的,升序排列应该放在最尾端
		len--;
		heapify(array, 0, len);
	}
}
void Heap_sort::buildMaxHeap(int array[], int len){
	//这里只能从下到上建堆
	for (int i = len / 2; i >= 0; i--){
		heapify(array, i, len);
	}
}

void Heap_sort::heapify(int array[], int i, int len){//调整堆的函数
	int left = 2 * i + 1;
	int right = 2 * i + 2;
	int maxindex = i;
	//找根节点和左右子节点中最大的
	if (left < len && array[left] > array[maxindex]){
		maxindex = left;
	}
	if (right < len && array[right] > array[maxindex]){
		maxindex = right;
	}
	if (i != maxindex){
		swap(array[i], array[maxindex]);
		heapify(array, maxindex, len);
	}
}
#endif

主函数如下:

int main(){
	int a[10] = { 4, 2, 1, 9, 22, 3, 44, 0, 10, -1 };
	Heap_sort mysort;
	mysort.heap_sort(a,10);

	for (int i = 0; i < 10; i++){
		cout << a[i] << " ";
	}
	cout << endl;
	system("pause");
	return 0;
}

就可以看到输出了。

复杂度分析:

假设堆高h为一整数,堆有n个节点,那么h = log2n
该堆有2h个叶子节点,由于叶子节点是最底层,所以无需下沉
倒数第二层的节点有2h-1个,该层节点下沉到叶子节点至多需要比较2次(兄弟节点的比较,父节点与较大的兄弟节点的比较),交换1次(如果父节点小于子节点则交换)
倒数第三层有2h-2个非叶子节点,他们下沉到倒数第二层之后还要继续下沉到叶子节点(倒数第三层的节点可能比叶子节点还小,所以还要下沉到叶子节点),所以对倒数第二层的节点其下沉至多需要比较4次,交换2次。
以此类推,倒数第n层的下沉所需操作数 = 该层节点数 * 该层节点下沉所需的操作数 ,前者为2h-n+1,后者为3(n-1)。
所以根节点作为倒数第h+1层,它的下沉所需操作数就为2h-(h+1)+1 * 3 (h+1-1) = 20 * 3h。
将各层的下沉所需操作数从上至下相加,就是构造整个有序堆的所需操作数:3 ( 20h + 21(h-1) + 22(h-2) +…2h-1 ),设该式为a,则2a - a = 3 ( 21 + 22 +…2h - h ),根据等比序列的求和公式,我们可以知道a = 3 ( 2h+1 - 2 - h),代入h,得a = 3(2n - 2 - log2n) ≈ 6n。
综上,建有序堆的时间复杂度是O(n)。
不断交换堆顶元素和堆底元素
共进行n-1次交换,所需操作数为n-1
并且每次交换都会导致根节点不再是堆中的最大元素,所以需要对新的根节点进行下沉操作,所需操作数为3(n-1)h,即3(n-1)log2n
综上,该过程所需操作数为n-1 + 3(n-1)log2n = (n-1)(3log2n + 1),时间复杂度即为O(nlogn)
所以时间复杂度是O(n logn)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值