排序算法之(7)--堆排序

介绍

堆排序(Heap Sort)就是利用堆(假设利用大顶堆)进行排序的方法。它的基本思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根结点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次小值。如此反复执行,便能得到一个有序序列了。

堆排序真的是一个好东西,不管是待排序序列有序无序,时间复杂度都基本一样。

注:这里堆是一种数据结构,而非内存的那个堆。可以参考完全二叉树,可以表示成一个数组。
这里写图片描述


代码

//堆排序
/*
这个堆是数据结构堆,不是内存malloc相关的那个堆--我曾经理解错n久
根节点比孩子节点都大的叫大顶堆(包括子树的根),比孩子节点小的叫小顶堆
从小到大排序用的是大顶堆,所以要先构造这种堆,然后把这个根的最大元素交换到最尾巴去
每次拿走一个最大元素,待排序的队列就慢慢变短
主要步骤1,初始化构造这种大顶堆,把堆顶最大的数放到最尾巴,数列长度减少1,再次构建大顶堆
2,这时候只有堆顶元素不满足大顶堆,那么其实只要从堆顶元素开始慢慢微调而已,没必要再完全重新建堆,想要也可以,不过很浪费时间
理解起来确实很难,涉及到完全二叉树
孩子节点i的爸爸是i/2,爸爸节点的儿子是2i和2i+1。
第一次初始化之后充分利用子树已经是大顶堆
*/
//辅助函数:交换两个变量
void swap(int*a,int*p)
{
    int temp = *a;
    *a = *p;
    *p = temp;
}

void adjust(int* arr,int len,int index)
{
    //调整函数,把孩子、父亲中的最大值放到父亲节点
    //index为待调整节点下标,一开始设它最大
    int max = index;
    int left = 2*index+1;//左孩子
    int right = 2*index+2;//右孩子
    if(left<len && arr[left] > arr[max])
    {
        max = left;
    }
    if(right<len && arr[right] > arr[max])
    {
        max = right;
    }
    //如果父亲节点不是最大
    if(max!=index)
    {
        //一旦上层节点影响了某个孩子节点,还要观察以这个孩子节点为父节点的子树是不是也不是大顶堆了
        swap(&arr[index],&arr[max]);
        //因为发生了交换,还要继续调整受到影响的孩子节点
        //***************************************
        adjust(arr,len,max);//这句话非常非常关键
        //***************************************
        /*
            只有父亲和孩子节点发生了交换,才有继续调整孩子的必要,如果无脑在不是这里面递归,堆排序的效果不会比冒泡好到哪去
            而如果写在了这里面,虽然还是pk不过快排,但好歹和快排的差距只缩小到个位数倍数的量级(小数据量的时候)
            堆排序一个优点是空间复杂度也不高
        */
    }
}
//主要排序部分
void heapSort(int* arr,int len)
{
    //初始化大顶堆
    //initHeap(arr,i,0);
    //从最后一个非叶子节点开始
    //第一次一定要从下至上一直排,一开始是乱序的
    int i = len/2-1;
    for(i;i>=0;i--)
    {
        adjust(arr,len,i);
    }
    swap(&arr[0],&arr[len-1]);


    //第二次之后,只需要从根节点从上到下调整,遇到没发生交换的直接可以退出循环了
    //微调得到大顶堆(因为只有堆顶不满足而已)
    int j = len -1; //去掉尾节点后的数组长度
    //把最大值交换到最后
    for(j;j>0;j--)
    {
        adjust(arr,j,0);
        swap(&arr[0],&arr[j-1]);
    }
}

猜你喜欢

转载自blog.csdn.net/hiudawn/article/details/80378105