C语言 堆排序

堆排序简介

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

堆排序的模拟过程可以看链接:HeapSortion

大根堆:大根堆要求根节点的关键字既大于或等于左子树的关键字值,又大于或等于右子树的关键字值
以下图片取自维基百科:
这里写图片描述

小根堆:其中任一非终端节点的数据值均不大于其左子节点和右子节点的值
以下图片取自维基百科:
这里写图片描述

实现过程

堆排序思路:
1. 将数组构建成大根堆
2. 将根节点与最后一个数字交换,重新构建堆

构建大根堆:

首先,我们需要知道堆中父节点与子节点之间的关系:
现在有一个数组a[5], 构建成堆的话将如下图所示:
这里写图片描述
很明显:
如果节点为 i, 则它的父节点为i/2 + 1或者i/2 + 2, 取决于 i 是偶数还是奇数
它的子节点为2*i + 1或者2*i+2

为了比较容易地理解插入排序,我们可以列出一组数据,比如:
1,7,6,8,0

  1. 将数组构建成大根堆
    这里写图片描述

至此大根堆构建完成:
这里写图片描述

  1. 将根节点与最后一个数字交换
    这里写图片描述

代码实现

#include <stdio.h>

void print(const int *a, const int length) {
    int i;
    for (i = 0; i<length; i++) {
        printf("%d ", a[i]);
    }
    putchar('\n');
}

void exchange(int *a, const int i, const int j) {
    int tmp = *(a + i);
    *(a + i) = *(a + j);
    *(a + j) = tmp;
}

int getPar(int pos) {
//父节点地址
    int par_pos;
    if (pos == 0)
        return 0;
    if (pos % 2 == 0) { //偶数
        par_pos = pos / 2 - 1;
    }
    else {  //奇数
        par_pos = pos / 2;
    }
    return par_pos;
}

void _heap(int *a, int low, int high) {
    if (low <= high) {
        //与子节点比较,重建大根堆
        int child_1 = low * 2 + 1;
        int child_2 = low * 2 + 2;
        if (child_2 <= high) {
            if (a[child_1] > a[child_2]) {
                if (a[child_1] > a[low]) {
                    exchange(a, child_1, low);
                    _heap(a, child_1, high);
                }
            }
            else {
                if (a[child_2] > a[low]) {
                    exchange(a, child_2, low);
                    _heap(a, child_2, high);
                }
            }
        }
        else if (child_1 <= high && child_2 > high) {
            if (a[child_1] > a[low]) {
                exchange(a, child_1, low);
                _heap(a, child_1, high);
            }
        }
    }
}

void heapSort(int *a, const int low, const int high) {
    //1.构建大根堆,排序
    for (int i = high; i > 0; i--) {
        int parent = getPar(i);
        int bro;
        if (i % 2 == 0) {//偶数
            bro = i - 1;
            if (a[bro] > a[i]) {
                if (a[parent] < a[bro]) {
                    exchange(a, bro, parent);

                    _heap(a, bro, high);
                }
            }
            else {
                if (a[parent] < a[i]) {
                    exchange(a, i, parent);

                    _heap(a, i, high);
                }
            }
            i--;
        }
        else { //奇数
            if (a[parent] < a[i]) {
                exchange(a, parent, i);

                _heap(a, parent, high);
            }
        }
    }
    print(a, high - low + 1);

    //2.与最后一个数交换
    for (int i = high; i > 0; i--) {
            exchange(a, i, 0);

        //重排
        //print(a, high - low + 1);
        _heap(a, 0, i-1);  //不是i,而是i-1

    }
}


void main() {
    const int length = 4;
    int my_array[5] = { 1 ,7, 6, 8, 0 };

    heapSort(my_array, 0, length-1);
    print(my_array, length);
}


代码精简

void HeapSortBase(int *a, const int length){
    for(int i=0; i<length; i++){
        int dad = i; 
        int dad_father;
        //两个子节点与父节点分别进行比较
        for(int plus=1; plus<3; plus++){
            int son = 2*dad+plus;
            // 父节点大于子节点
            if(a[dad]>a[son] && son<length){
                swap(&a[dad], &a[son]);   
                //判断父节点是否小于它的父节点
                do{
                    dad_father = (dad-1)/2;
                    swap(&a[dad_father], &a[dad]);
                }while(a[dad_father]>a[dad] && dad_father>=0);              
            }
        }

    }
}

void HeapSort(int *a, const int length){
    for(int i=0; i<length; i++){
        //父节点是最小的
        HeapSortBase(a+i, length-i);
    }
}

猜你喜欢

转载自blog.csdn.net/fpk2014/article/details/80461528