堆排序算法分析
一、简介
- 堆排序算法是利用堆进行排序的方法,基本思想为:将待排序的序列构造成一个小(大)堆,此时,堆顶的元素就是整个序列的最小值(最大值),然后,将它移走(和堆数组末尾元素交换),然后将剩下的n-1个元素重新构建成一个堆,得到n个元素中的次小(大)堆,反复执行,就会得到一个有序序列了。
- 堆性质:
- 它是一个完全二叉树
- 小堆:每个结点的值都小于或等于其左右孩子的结点值
- 大堆:每个结点的值都大于或等于其左右孩子的结点值
- 排升序:建立大堆
- 排降序:建立小堆
二、复杂度计算
- 构建堆时间复杂度分析:从完全二叉树的最下层最右边的非叶子结点开始构建,将它与其他孩子进行比较和是否有必要交换。
假设高度为h的完全二叉树,有n个结点,第i层的高度为hi,第i层的元素个数ni = 2^(i-1),则第i层最坏的调整次数为:t(ni) = ni·hi,因此总时间复杂度t(n) = 1×(h-1) + 2 × (h-2)+…2^(h-2) ×1,该数列和为差比数列,运用错位相减法,可得到其和为:t(n) = 2^h - h - 1 = n - log(n+1)。因此:建堆的时间复杂度为:O(N)
- 堆排序时间复杂度分析:因为需要n-1次堆重建,每次循环的比较次数为log(i)—与高度相关,t(n) = log2+log3+…+log(n-1)+log(n) ≈ log(n!)。因为log(n!)和nlog(n)是同阶函数,因此:
堆排序的时间复杂度为:O(N*logN)
三、代码实现(c语言)
该代码旨在排降序(小堆)。
- heap.h
//heap.h
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
typedef int HPDataType;
typedef struct Heap {
HPDataType* _a;
int _size;
int _capacity;
}Heap;
//堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n);
//向下调整
void AdjustDown(HPDataType* a, int n, int root);
//向上调整
void AdjustUp(HPDataType* a,int n, int child);
//堆的销毁
void HeapDestory(Heap* hp);
//堆的插入
void HeapPush(Heap* hp, HPDataType x);
//堆的删除
void HeapPop(Heap* hp);
//取堆顶的数据
HPDataType HeapTop(Heap* hp);
//堆数据个数
int HeapSize(Heap* hp);
//堆的判空
int HeapEmpty(Heap* hp);
//交换数据
void Swap(HPDataType* t1, HPDataType* t2);
//堆数组进行堆排序
void HeapSort(int* a, int n);
//TopK问题
void TestTopK(Heap* hp, int k);
void PrintTopK(Heap* hp, int n, int k);
- heap.c
//heap.c
#include"Heap.h"
//堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n)
{
hp->_a = (HPDataType*)malloc(sizeof(HPDataType) * n);
//void *memcpy(void *destin, void *source, unsigned n);
assert(hp->_a);
memcpy(hp->_a, a, sizeof(HPDataType) * n);
hp->_size = n;
hp->_capacity = n;
//向下调整法,从非叶子结点开始,建立小堆
for (int i = (n - 2) / 2; i >= 0; --i) {
AdjustDown(hp->_a,hp->_size,i);
}
}
//向下调整
void AdjustDown(HPDataType* a, int n, int root) {
int parent = root;
int child = parent * 2 + 1;
while (child < n) {
//找出最小的孩子
if (child + 1 < n && a[child] > a[child + 1]) {
child++;
}
//判断父节点是否需要调整
if (a[parent] > a[child]) {
Swap(&a[parent], &a[child]);
parent = child;
child = parent * 2 + 1;
}
else {
break;
}
}
}
//向上调整
void AdjustUp(HPDataType* a, int n, int child) {
int parent = (child - 1) / 2;
//parent > 0 是错的
while (child > 0) {
if (a[parent] > a[child]) {
Swap(&a[parent], &a[child]);
child = parent;
parent = (child - 1) / 2;
}
else {
break;
}
}
}
//堆的销毁
void HeapDestory(Heap* hp) {
assert(hp);
free(hp->_a);
hp->_a = NULL;
hp->_capacity = hp->_size = 0;
}
//堆的插入
void HeapPush(Heap* hp, HPDataType x) {
assert(hp);
if (hp->_size == hp->_capacity) {
HPDataType* temp = (HPDataType*)realloc(hp->_a, sizeof(HPDataType) * hp->_capacity * 2);
assert(temp);
hp->_a = temp;
hp->_capacity *= 2;
}
hp->_a[hp->_size++] = x;
//调整堆--向上调整
AdjustUp(hp->_a, hp->_size, hp->_size - 1);
}
//堆的删除
void HeapPop(Heap* hp) {
assert(hp);
assert(hp->_size);
Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);
hp->_size--;
AdjustDown(hp->_a, hp->_size, 0);
}
//取堆顶的数据
HPDataType HeapTop(Heap* hp) {
assert(hp);
assert(hp->_size);
return hp->_a[0];
}
//堆数据个数
int HeapSize(Heap* hp) {
assert(hp);
return hp->_size;
}
//堆的判空
int HeapEmpty(Heap* hp) {
assert(hp);
return hp->_size == 0 ? 1 : 0;
}
//交换数据
void Swap(HPDataType* t1, HPDataType* t2) {
HPDataType temp = *t1;
*t1 = *t2;
*t2 = temp;
}
//堆数组进行堆排序
void HeapSort(int* a, int n) {
//建堆
for (int i = (n - 2) / 2; i >= 0; --i) {
AdjustDown(a, n, i);
}
//时间复杂度:O(N*logN)
//重建堆一共需要n-1次循环,每次循环的比较次数为log(i)
//向下调整,堆排序
int end = n - 1;
while (end > 0) {
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
end--;
}
}
//TopK问题
void TestTopK(Heap* hp, int k) {
//前k小的数字
//时间复杂度 O (K*logN)
while (k) {
HeapPop(hp);
k--;
}
}
void PrintTopK(Heap* hp, int n, int k) {
while (k) {
printf("%d ",hp->_a[n - k]);
k--;
}
}
- 测试代码
//test.c
#include"Heap.h"
int main() {
int a[] = { 27, 15, 19, 18, 28, 34, 65, 49, 25, 37 };
//HeapSort(a, sizeof(a) / sizeof(HPDataType));
Heap hp;
HeapCreate(&hp, a, sizeof(a) / sizeof(HPDataType));
//HeapPush(&hp, 13);
TestTopK(&hp, 5);
PrintTopK(&hp, sizeof(a) / sizeof(HPDataType), 5);
HeapDestory(&hp);
return 0;
}
github链接 https://github.com/Xiaowei7858/Code-notes.git