排序算法5——图解堆排序及其实现

排序算法1——图解冒泡排序及其实现(三种方法,基于模板及函数指针)
排序算法2——图解简单选择排序及其实现
排序算法3——图解直接插入排序以及折半(二分)插入排序及其实现
排序算法4——图解希尔排序及其实现
排序算法5——图解堆排序及其实现
排序算法6——图解归并排序及其递归与非递归实现
排序算法7——图解快速排序以及不同CUTOFF的时间测试


堆的概念和性质

堆,是一种特殊的二叉树,每个子结点的值总是小于(或者大于)它的父结点,相应的分为最大堆最小堆

堆,是一个完全二叉树,一般情况下堆排序都是用数组的方式实现
在这里插入图片描述
这里可以看到,若以0开始编号
大顶堆arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

下面的性质是最重要的:
若以0开始编号,对编号为 i 的结点,其左孩子为2i+1,右孩子为2i+2,二者相差为1
第一个非叶子结点编号为(int)(length/2)-1
最后一个结点的编号为length-1

堆排序的基本思想

利用最大堆(对应升序)或者最小堆(对应降序)输出堆顶元素,即最大值(或最小值),
然后将剩下元素重新生成最大堆(或者最小堆),继续输出堆顶元素,重复此过程直到全部元素都已输出即可得到有序序列

空间复杂度为O(N)的方法

额外开辟一个辅助的数组空间,将堆顶元素逐一放入辅助数组里,最后再把辅助数组的内容赋值回原始的数组

空间复杂度为O(1)的方法

这个方法的时间复杂度与前一种相同,都是O(NlogN),但是不需要额外的辅助数组,所以空间复杂度为O(1)
但堆排序是不稳定排序

主要的步骤是:
1.先将一个无序的序列生成一个最大堆
2.将堆顶元素与堆的最后一个元素对换位置
3.将剩余的元素重新生成一个最大堆
4.重复2-3步骤,直到堆中只剩一个元素

首先介绍给出一个待排序的无序序列,并以完全二叉树的形式将其绘制出来
在这里插入图片描述
可以看到,该无序序列并不是一个最大堆

于是,做上述步骤的第一步,我们将其变为一个最大堆
那么,怎么变呢?我们需要从第一个非叶子节点开始,也就是编号为length/2-1的那个结点
针对上面的例子,length/2-1也就是5/2-1=1
下面,一图带你读懂最大堆的构建
在这里插入图片描述

下面,我们执行上述的步骤2和步骤3
2.将堆顶元素与堆的最后一个元素对换位置
3.将剩余的元素重新生成一个最大堆
在这里插入图片描述

从上面可以看到,堆排序的整体主要由两部分组成
1.构建初始堆,时间复杂度为O(N)
2.交换堆顶和末尾元素并对剩余元素重建最大堆,重建堆的时间复杂度为O(NlogN)

所以总体来说,堆排序的时间复杂度为O(NlogN)
它对原始记录的排序状态并不敏感,最好最坏和平均时间复杂度都是O(NlogN)
在空间上,只有一个用来交换的暂存单元,空间复杂度也很好
由于记录的比较与交换是跳跃式进行,因此也是不稳定的。

由于初始构建所需的比较次数较多,因此不适合待排序序列较少的情况

测试结果及代码

在这里插入图片描述

#include <iostream>

template<class T>
void adjustHeap(T A[], int current, int length) {
	int parent = current;
	T tmp = A[current];
	for (int child = 2 * parent + 1; child < length;) {
		if (child != length - 1 && A[child] < A[child + 1]) {
			++child;
		}
		if (tmp < A[child]) {
			A[parent] = A[child];
			parent = child;
			child = 2 * child + 1;
		}
		else
			break;
	}
	A[parent] = tmp;
}

template<class T>
void HeapSort(T A[], int length) {
	/// 根据输入数组建立最大堆
	for (int i = length / 2 - 1; i >= 0; --i) {
		adjustHeap(A, i, length);
	}

	T tmp;
	for (int i = length - 1; i > 0; --i) {
		tmp = A[0];
		A[0] = A[i];
		A[i] = tmp;
		/// 交换以后再以0位置的结点重建最大堆,同时元素个数-1
		adjustHeap(A, 0, i);
	}

}

template<class T>
void ArrShow(T *A, int length) {
	for (int i = 0; i < length; ++i) {
		std::cout << A[i] << " ";
	}
	puts("\n");
}

int main(int argc, char *argv[]) {
	int test[9] = { 1, 2, 7, 3, 4, 6, 8, 5, 9 };
	ArrShow(test, 9);

	puts("HeapSort : ");
	HeapSort(test, 9);
	ArrShow(test, 9);


	return 0;
}

猜你喜欢

转载自blog.csdn.net/CSDN_dzh/article/details/84979116
今日推荐