十大排序算法 之 堆排序

107 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第19天,点击查看活动详情

堆排序:借助二叉堆的思想,实现数据的排序。其中使用的堆是大顶堆或者小顶堆。

  1. 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
  2. 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

image.png

找到最后一个非叶子结点,索引值计算方式:inx=ArrCnt/2-1。
计算得到父节点索引,而他的子索引值为2*inx+1 和2*inx+2。

待排序序列一层层的按照二叉树结构构建一棵树。最后一层的第一个对象的序列号为

思想: 进行大顶堆调整,把无序序列构造成一个大顶堆,根据大顶堆概念,然后就得到了一个最大的数值;重复此过程,直到只剩下最后一个对象,就完成了排序。

 堆排序的平均、最差和最好时间复杂度都是O(nlogn),是不稳定的排序。
 

流程:

  1. 构建大顶堆。 待排序序列构建一个二叉堆,找到最后一个非叶子结点,和其子节点比较,如果子节点大于父节点,则交换位置。 循环这个过程,直到完成所有非叶子结点的比较交换,然后得到一个大顶堆。
  2. 根结点(最大值)与最尾对象交换,也就是有序区,有序区不参与构建二叉堆。
  3. 使用余下n-1序列构建大顶堆,交换根节点和倒数第二个对象,扩大有序区。
  4. 循环执行123,最终完成排序。

代码示例

#include<stdio.h>
#include <stdlib.h>
#include<unistd.h>
#include <string.h>
#include <errno.h>

#define SWAP(a1, a2, type) do {type tempNoI = a1; a1 = a2; a2 = tempNoI;} while(0)



int ArrCnt = 0;
int NumArr[200] = {0};

void PrintNums()
{
    for(int i = 0;i < ArrCnt;i++){
        fprintf(stdout," %d-",NumArr[i]);
    }
}

//升序排序
int HeapSort(int Cnt,int* ArrPtr)
{
    int temp = 0;
    int InxLeft ,InxRight;
    fprintf(stdout," HeapSort cnt[%d]\n",Cnt);

    while(1 < Cnt){

        //构建大顶堆
        for(int i = Cnt/2-1;0 <= i;i--){
            InxLeft = 2*i+1;
            InxRight = 2*i+2;
            if(InxRight < Cnt){
                temp = ArrPtr[InxLeft] > ArrPtr[InxRight]?InxLeft:InxRight;
            } else{
                temp = InxLeft;
            }
         //  fprintf(stdout," 构建大顶堆 i[%d]:InxLeft[%d]InxRight[%d]temp[%d]\n",i,InxLeft,InxRight,temp);
         //   fprintf(stdout,"\tvalue i[%d]:InxLeft[%d]InxRight[%d]\n",ArrPtr[i],ArrPtr[InxLeft],ArrPtr[InxRight]);

            if(ArrPtr[i] < ArrPtr[temp]){
               // fprintf(stdout,"\t exchange: i[%d]=[%d]   temp[%d]=[%d]\n",i,ArrPtr[i],temp,ArrPtr[temp]);
                SWAP(ArrPtr[i],ArrPtr[temp],int);
            } 
        }
        Cnt--;
       // fprintf(stdout," SWAP arr[%d]=[%d]\n\n",Cnt,NumArr[0]);
        SWAP(ArrPtr[0],ArrPtr[Cnt],int);
    }
    return 0;
}
int main(int argc, char** argv)
{
    if(argc == 1) {
        fprintf(stderr,"%s:need a some arg\n",argv[0]);
        exit(1);
    }
    int CntTemp = ArrCnt = argc -1;

    for(int i = 0;i < ArrCnt;i++){
        NumArr[i] = atoi(argv[i+1]);
    }
   
    fprintf(stdout,"\t\t\t\t[Heap Sort]inputNum:");
    PrintNums();
    fprintf(stdout,"\n");

    HeapSort(ArrCnt,NumArr);

    fprintf(stdout,"\n\t\t\t\tresult:");
    PrintNums();
    fprintf(stdout,"\n");
    return 0;

}

测试结果如下 image.png

其中出现了小问题,调查了两个小时。宏中了用了temp中间变量,而在代码中有声明了了同名变量,因为宏是在预编译期间展开的,所以说实际上代码有了俩同名变量,结果就是代码是对的,结果总是错误的。