堆排序是利用堆的堆序性,对于最小堆而言,最小元素在堆顶,对于一个数组先通过将其建立成一个最小堆 然后一个一个删除其堆顶元素既实现了排序。
当堆的顶部最小元素被删除后要对堆做调整使其再次满足堆序性。由于对堆做一次调整最坏时间复杂度为O(logN),堆排序需要对堆做N-1次调整 所以堆排序最坏时间复杂度为O(NlogN),不过其比快排多开辟了一个堆的存储空间。
我们简要分析下堆排序的时间复杂度。我们在每次删除最小元素时都需要对堆做重新调整,这样,每次重新调整堆的时间复杂度变为O(logn),而堆排序时有n-1次重新调整堆的操作,建堆时有((len-1)/2+1)次重新调整堆的操作,因此堆排序的平均时间复杂度为O(n*logn), 最坏时间复杂度也为O(n*logn)
堆排序在排序元素较少时有点大才小用,待排序列元素较多时,堆排序还是很有效的。另外,堆排序在最坏情况下,时间复杂度也为O(n*logn)。相对于快速排序(平均时间复杂度为O(n*logn),最坏情况下为O(n*n)),这是堆排序的最大优点。
堆排序的详细实现如下:
//
// sort.c
// test_sort
//
//
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
typedef int ElementType;
#define kMinSentinelElement (-1024)
typedef struct _priorityQueue{
int capacity;
int size;
ElementType *elements;
}HeapStruct, *PriorityQueue;
PriorityQueue initialize(int maxElementsCount);
bool insert(PriorityQueue h, ElementType d);
bool deleteMin(PriorityQueue h);
void traverse(PriorityQueue h);
ElementType findMin(PriorityQueue h);
ElementType findTail( PriorityQueue h);
void heapSort(int arr[], int length);
PriorityQueue initialize(int maxElementsCount){
PriorityQueue priorityq = calloc(1, sizeof(HeapStruct));
//add one extra for sentinel sentinel
ElementType *elements = calloc((maxElementsCount + 1), sizeof(ElementType));
priorityq->elements = elements;
priorityq->capacity = maxElementsCount;
priorityq->size = 0;
priorityq->elements[0] = kMinSentinelElement;
return priorityq;
}
bool insert(PriorityQueue h, ElementType d){
if (h->size + 1 > h->capacity) {
printf("capacity is big\n");
return false;
}
int size = h->size;
if (size == 0) {
h->elements[1] = d;
h->size++;
return true;
}
if (size > 0){
//得到当前要插入的数据的位置
//和其父节点进行比较 判断是否需要交换
//一直判断到父节点的数据满足条件
//当前要插入的数据在数组中的位置
int insertPosition = size + 1;
int fatherNodePosition = floor(insertPosition/2);
if (h->elements[fatherNodePosition] < d) {
h->elements[insertPosition] = d;
h->size++;
}else{
while (h->elements[fatherNodePosition] > d) {
//将父节点的数据赋给当前插入的节点
h->elements[insertPosition] = h->elements[fatherNodePosition];
insertPosition = fatherNodePosition;
fatherNodePosition = floor(fatherNodePosition/2);
}
//将当前要插入的数据放入其父节点
h->elements[insertPosition] = d;
h->size++;
}
}
return true;
}
bool deleteMinFunction1(PriorityQueue h){
//put the last element to top
//compare top to (left and right
//when top is small (left and right) stop
//when top is big (left or right) put (left or right samll) to top
//put tail to (left or right samll) go on
//此方法在判断左右子节点时方法较为繁琐
ElementType tail = h->elements[h->size];
h->size--;
for (int i = 1; i <= h->size; ) {
int leftp = i*2;
int rightp = i*2 + 1;
if (leftp > h->size) {
//the last position
h->elements[i] = tail;
return true;
}
if (rightp > h->size && leftp <= h->size) {
//just one left immediate change
if (tail > h->elements[leftp]) {
h->elements[i] = h->elements[leftp];
h->elements[leftp] = tail;
}else{
h->elements[i] = tail;
}
return true;
}
if (tail < h->elements[leftp] && tail < h->elements[rightp]) {
h->elements[i] = tail;
return true;
}else {
int position = h->elements[leftp] < h->elements[rightp] ? leftp : rightp;
h->elements[i] = h->elements[position];
i = position;
}
}
return false;
}
//对二叉堆按照(结构性和堆序性要求)进行调整
void rebulid(PriorityQueue h, int topPosition){
//put the last element to top
//compare top to (left and right
//when top is small left and right stop
//when top is big (left or right) put (left or right samll) to top
//put tail to (left or right samll) go on
ElementType top = h->elements[topPosition];
int lp = topPosition * 2;
int rp = topPosition * 2 + 1;
if (lp > h->size) {
return;
}
if (rp > h->size || lp == h->size) {
//only left
ElementType l = h->elements[lp];
if (l < top) {
//change
h->elements[lp] = top;
h->elements[topPosition] = l;
}
return;
}
ElementType l = h->elements[lp];
ElementType r = h->elements[rp];
if (top < l && top < r) {
return;
}
int rlpsmall = l < r ? lp : rp;
h->elements[topPosition] = h->elements[rlpsmall];
h->elements[rlpsmall] = top;
rebulid(h, rlpsmall);
}
bool deleteMinByFunction2(PriorityQueue h){
//此方法采用递归一直向下过滤
ElementType tail = h->elements[h->size];
h->elements[1] = tail;
h->size--;
rebulid(h, 1);
return true;
}
/* 此方法实现较为简洁 */
bool deleteMin(PriorityQueue h){
/*
当删除一个最小元时,在根节点处产生了一个空穴,由于现在堆少了一个元素,因此堆
中最后一个元素X必须移动到该堆的某个位置,如果X可以被放到空穴中,那么deleteMin操作完成,不过这一般是不太可能的。因此我们将空穴的两个儿子中较小者
移到空穴,这样就把空穴向下推了一层,重复该步骤直到X可以被放入空穴中。
*/
ElementType tail = h->elements[h->size];
h->size--;
for (int i = 1; i <= h->size; ) {
//find left and right min position
int minPosition = 0;
if ((2 * i + 1) <= h->size) {
minPosition = h->elements[2 * i] < h->elements[2 * i + 1] ? (2 * i) : (2 * i + 1);
}else if (2 * i <= h->size){
minPosition = 2 * i;
}else{
//the last position no (left and right)
h->elements[i] = tail;
return true;//compare end
}
if (tail < h->elements[minPosition]) {
h->elements[i] = tail;
return true;//compare end
}else{
h->elements[i] = h->elements[minPosition];
i = minPosition;
}
}
return false;
}
//下滤
void percolateDown( PriorityQueue h, int position){
if (position > h->size) {
return;
}
ElementType top = h->elements[position];
int i = position;
//find left and right min position
int minPosition = 0;
if ((2 * i + 1) <= h->size) {
minPosition = h->elements[2 * i] < h->elements[2 * i + 1] ? (2 * i) : (2 * i + 1);
}else if (2 * i <= h->size){
minPosition = 2 * i;
}else{
//the last position no (left and right)
return;
}
if (top > h->elements[minPosition]) {
h->elements[i] = h->elements[minPosition];
h->elements[minPosition] = top;
percolateDown(h, minPosition);
}
}
ElementType findTail( PriorityQueue h){
int size = h->size;
if (size <= h->capacity) {
printf("the findTail is %d\n", h->elements[size]);
return h->elements[size];
}
return kMinSentinelElement;
}
ElementType findMin(PriorityQueue h){
if (h->size > 0) {
return h->elements[1];
}
return kMinSentinelElement;
}
//遍历
void traverse(PriorityQueue h){
int size = h->size;
for (int i = 0; (2*i) <= size; i++) {
if (i != 0) {
printf("the element is %d\n", h->elements[2*i]);
}
if (2*i+1 <= size) {
printf("the element is %d\n", h->elements[2*i + 1]);
}
}
printf("\n");
for (int i = 0; 2*i <= size; i++) {
if (i != 0) {
printf("the left element is %d\n", h->elements[2*i]);
}
}
printf("\n");
for (int i = 0; (2*i+1) <= size; i++) {
if (i != 0) {
printf("the right element is %d\n", h->elements[2*i+1]);
}
}
printf("\n");
}
void testInfoMessage(){
PriorityQueue priorityq = initialize(15);
insert(priorityq, 1);
traverse(priorityq);
insert(priorityq, 2);
insert(priorityq, 3);
insert(priorityq, 4);
insert(priorityq, 5);
// insert(priorityq, 2);
// insert(priorityq, 4);
// insert(priorityq, 3);
// insert(priorityq, 5);
// insert(priorityq, 4);
// insert(priorityq, 8);
// insert(priorityq, 12);
// insert(priorityq, 11);
// insert(priorityq, 4);
// insert(priorityq, 8);
// insert(priorityq, 12);
// insert(priorityq, 3);
// insert(priorityq, 7);
// insert(priorityq, 6);
// insert(priorityq, 5);
// insert(priorityq, 1);
// insert(priorityq, 9);
traverse(priorityq);
deleteMinFunction1(priorityq);
traverse(priorityq);
findTail(priorityq);
deleteMinFunction1(priorityq);
traverse(priorityq);
deleteMinFunction1(priorityq);
traverse(priorityq);
int arrNew[] = {49, 38, 65, 97, 26, 13, 27, 49, 55, 4};
// int arrNew[] = {49, 38, 65, 97, 26, 13, 27, 55, 4};
heapSort(arrNew, sizeof(arrNew)/sizeof(int));
}
//堆排序
void heapSort(int arr[], int length){
//根据数组创建堆
#if 0 - 逐个插入
PriorityQueue priorityq = initialize(length);
for (int i = 0; i < length; i++) {
insert(priorityq, arr[i]);
}
traverse(priorityq);
#endif
#if 1 - 逐渐向下过滤
PriorityQueue priorityq = initialize(length);
for (int i = 0; i < length; i++) {
priorityq->elements[i+1] = arr[i];
}
priorityq->size = length;
traverse(priorityq);
for (int i = length / 2; i > 0; i--) {
percolateDown(priorityq, i);
}
traverse(priorityq);
#endif
for (int i = 0; i < length; i++) {
ElementType element = findMin(priorityq);
arr[i] = element;
//deleteMin(priorityq);
deleteMinFunction1(priorityq);
// deleteMinByFunction2(priorityq);
}
for (int i = 0; i < length; i++) {
printf("the arr[%d] is %d\n", i, arr[i]);
}
}