#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)
1111

被折叠的 条评论
为什么被折叠?



