Heap Sort

#include <stdlib.h>
#include <time.h>
#include <stdio.h>

typedef int ElemType;

//定义 动态顺序表 结构体类型
typedef struct {
    ElemType *elem;
    int length;
}DSTable;

//初始化 动态顺序表
void Init_DSTable(DSTable &L,int len){
    L.length=len;
    L.elem=(ElemType*)malloc(sizeof (ElemType)*L.length);

    //随机数生成
    int i;
    srand(time((NULL)));
    for(i=0;i<L.length;i++){
        L.elem[i]=rand()%100;
    }
}

//堆排序
void swap(ElemType &a,ElemType &b){
    ElemType temp;
    temp=a;
    a=b;
    b=temp;
}

    //改造 以dad为根的 一颗子树,使其成为大根堆。 (***堆排序的关键)
void AdjustDown(DSTable L,int dad,int len){   //切记:形参len必不可少,len表示要进行排序的元素个数!!!
    int son=2*dad+1;  //找到dad的孩子
    while (son<len){

        //第一个if语句作用:比较其孩子,找到它孩子的最大值。
        if(son+1<len&&L.elem[son]<L.elem[son+1]){
            son++;
        }

        //第二个if-else语句:
        //1.dad是从最后一个父亲节点开始,从后向前遍历的。故 前x个节点 其左右两边都只有一个节点。
        //  dad 和 其最大的孩子 作比较。如果dad大,则 以它(dad)为根的子树(注意是子树)就是大根堆。————执行else里的语句:break; 结束循环。
        //  若dad更小,则 交换元素值 ,这时 dad的位置 已然最大。便实现了子树的大根堆!!!( 与(2.)对比 )
        //  当然,实现成为大根堆后,代码往后运行(执行 dad=son; son=2*dad+1;)。
        //  开始第二轮循环,这时会发现son的数值已经超过数据长度。————不满足循环内条件判断语句,循环结束。
        // 2.循环(Heap_Sort中第一个for循环)依次进行,直到 以第y个节点作为 dad 的子树(注意是子树)左右两边 并不只有一个节点,也是一棵子树!
        //   根据之前的循环改造,dad的左右子树 一定是大根堆!但是以dad为根的子树不一定是大根堆!
        //   因为还没有判断 现在的dad 与 其两个孩子节点的大小。此次循环的目的就是 将其(以dad为根的子树),变成大根堆。
        //   接下来就是重点了,如果 dad < max(左孩子,右孩子),显然以它为根的子树(注意是子树) 就是大根堆。————执行else里的语句:break; 结束循环。
        //   如果dad < max(左孩子,右孩子)(第一个if语句找到了son——即 max(左孩子,右孩子) 的位置)
        //   则 将dad位置的元素值 与 son 位置的元素值交换。这时 dad的位置 已然最大。但是不一定实现大根堆!!!( 与(1.)对比 )
        //   因为刚刚 与 dad位置 发生交换的那一边的子树 不一定是大根堆了。(原因也就是发生了元素值的交换)
        //   这时,代码往后运行(执行 dad=son; son=2*dad+1;),这两条语句就发挥上它的关键的作用了。
        //   继续将 发生交换的那一边的子树 调整至大根堆,直至 不满足循环内条件判断语句,循环结束。——这时以dad作为根的*子树*改造成大根堆。

        //综上,第二个if-else语句,简而言之就是,不发生元素值的交换的子树已然是大根堆,直接break结束循环;
        //发生了元素值的交换 就要进行 2、3、4···多轮的循环改造,直至 不满足循环内条件判断语句,该子树才是大根堆,循环结束。
        //第一个if条件是为了给第二个if-else语句做好前期准备。
        if(L.elem[dad]<L.elem[son]){
            swap(L.elem[dad],L.elem[son]);
            dad=son;
            son=2*dad+1;
        } else{
            break;
        }
    }

}

void Heap_Sort(DSTable L){
    int i;

    for(i=L.length/2-1;i>=0;i--){   //外层循环作用:循环利用 AdjustDown 将整个数组,改造成大根堆。

        // AdjustDown 有且仅将 一颗子树 改造成大根堆。(切记:在实现AdjustDown中,仅实现一棵子树的大根堆改造。)
        AdjustDown(L,i,L.length);  //i:表示以元素i 为根的子树,使其成为大根堆
    }

    //第一个for循环,实现了整个数组的大根堆,该堆的根,就是数组的最大元素,便可以这个数的位置
    swap(L.elem[0],L.elem[L.length-1]); //将最大的数放到数组最后,一个数有序。(数组长度-1)个元素无序

    //swap后(发生了元素的交换),显然这棵树不是大根堆了,但是其左右两边仍是大根堆!!!
    //所以只需改造以L.elem[0]元素为跟的树,但是***一定记得:已经排序好一个元素了,下一次仅需要排(数组长度-1)个元素!!!
    for(i=L.length-1;i>0;i--){ //i不能等于0,否则L.elem[i-1] 越界访问。 循环条件语句也可以先写成 i>1;
        AdjustDown(L,0,i); //每一次排序好一个元素,i会减少。i表示要进行排序的元素个数。
        swap(L.elem[0],L.elem[i-1]);
        L.length--;
    }

}
//打印顺序表
void Print_DSTable(DSTable L){
    int i;
    for(i=0;i<L.length;i++){
        printf("%3d",L.elem[i]);
    }
    printf("\n");
}
int main(){
    DSTable L;//声明动态顺序表
    Init_DSTable(L,10);
    int arr[10]={96,49,83,79,65,24,82,58,74,38};
    L.elem=arr;
    Print_DSTable(L);

    Heap_Sort(L);
    Print_DSTable(L);
    return 0;
}

时间复杂度:

T(n)=O( nlog2(n) )

空间复杂度:

S(n)=O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值