堆排序详解

堆排序是利用堆的堆序性,对于最小堆而言,最小元素在堆顶,对于一个数组先通过将其建立成一个最小堆 然后一个一个删除其堆顶元素既实现了排序。

当堆的顶部最小元素被删除后要对堆做调整使其再次满足堆序性。由于对堆做一次调整最坏时间复杂度为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]);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值