(图源:大话数据结构)
好文分享:排序算法解析:https://blog.csdn.net/kexuanxiu1163/article/details/103051357
目录
0准备工作
保存排序内容的自定义结构体,其中顺序表的长度,不算哨兵(下标为零的部分)
#define MAX 10
typedef struct SqlistData
{
int r[MAX+1];//把r[0]空出来当哨兵或者临时变量
int length;//记录长度,因为0被空出来了,所以长度和下标此时统一了
}SqList;
交换接口
void swap(SqList * L,int i,int j)//交换
{
int temp = L->r[i];
L->r[i] = L->r[j];
L->r[j] = temp;
}
打印接口
void print(SqList * L)
{
qDebug()<<"开始打印";
for(int i = 1;i<=L->length;++i)
{
qDebug()<<L->r[i];
}
}
顺序表的储存结构如下:
零位下标什么也不储存,所以无论是遍历还是排序,都是从下标1开始,顺序表长度为5,小标和顺序表中的元素序号完全一致。
1堆
堆: 具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,成为大顶堆,如图1
或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆
下面是很重要的关于完全二叉树的性质:
2堆排序算法
综上可以看出,要比就要比到叶子结点
void HeapSort(SqList * L)
{
int i;
for(i=L->length/2;i>0;i--)
HeapAdjust(L,i,L->length);
for(i = L->length;i>1;i--)
{
swap(L,1,i);
HeapAdjust(L,1,i-1);
}
}
整体思路非常清晰:
- 构建一个大顶堆
- 将最大值放在顺序表末尾swap(L,1,i),i从L->length开始
- 之后将剩余的数字调整为一个大顶堆
从上面的程序显然能看出这一点:
首先是构建一个大顶堆 HeapAdjust(L,i,L->length);
该接口后续后介绍,第二个参数是根结点的编号,第三个参数是末尾结点的编号
第二步:将已经变成大顶堆的首号(1)和末尾号进行交换
之后,最大的数字目前已经在末尾了
将剩余的数字,继续变成一个大顶堆, HeapAdjust(L,1,i-1);
第二个参数是i-1,外侧一直如此循环,直到编号为2,进行最后一次排序。
下面介绍将普通顺序表变成大顶堆的接口函数:
void HeapAdjust(SqList * L,int s,int m)
{
int temp,j;
temp = L->r[s];
for(j = 2*s;j<=m;j*=2)//从根的左孩子开始,从上到下,遍历所有的左孩子,知道左叶子结点(j>m)
{
if(j<m&&L->r[j]<L->r[j+1])//选出左右孩子中最大的
++j;
if(temp>=L->r[j])//根比最大的都大,那不操作
break;
L->r[s] = L->r[j];//将大的放到根的位置
s = j;//记录下谁替换了根,新的左孩子,也是下一个循环新的根
}
L->r[s] = temp;//最后将比较完的部分替换回去
}
接口需要输入三个参数:第一个,顺序表地址,第二个,根结点编号,第三个,尾部截止编号
根据堆的性质,根结点为s,左孩子就是2s,右孩子就是2s+1,当然了前提是2s和2s+1都小于M,也就是树的末尾结点编号
程序从根结点的左孩子入手,先比较该根结点的左右孩子谁大,之后比较这个最大的孩子和根的值谁大,谁大谁做根
然后左孩子是叶子结点函数还是其他,则需继续循环,下面是图文解释:
(图源:大话数据结构)
具体操作:
SqList Data3 = {{0,50,10,90,30,70,40,80,60,20},9};
print(&Data3);
HeapSort(&Data3);
qDebug()<<"简单的选择排序后";
print(&Data3);
输出: