(图源:大话数据结构)
好文分享:排序算法解析: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直接插入排序
进行排序的顺序表
SqList Data = {{0,9,1,5,8,3,7,4,6,2},9};
排序代码:
void InsertSort(SqList * L)
{
int i,j;
for(i = 2;i<=L->length;++i)
{
if(L->r[i]<L->r[i-1])//后面的小,需要插入,后面的要是大了就可以不管了
{
L->r[0] = L->r[i];//0位被当成了哨兵处理,暂时存放要交换的数据,相当于temp
for(j = i-1;L->r[j]>L->r[0];--j)//因为L->r[j]不断往前,迟早会碰见0,也就是L->r[j]==L->r[0],自然跳出
{
L->r[j+1] = L->r[j];//要插入的元素是[0],有序数列的末尾是j(i-1),比[0]大了,就换,之后继续和[0]比较
}
L->r[j+1] = L->r[0];//最后把[0]放入队列中即可
//放外面少移动,减少操作
}
}
}
代码讲解:
插入排序将整个顺序表看成两个部分,有序数列和等待插入部分
一般默认将第一个元素看成是“有序数列”
后面其他的都是“等待插入部分”
如图所示,5为有序部分,其他为等待插入部分
外循环从小标2开始进行循环
“等待插入部分”的首元素,和有序部分的末尾元素进行比较,如果大,则自动成为新的末尾
如果小于,则往前放,知道新插入的元素找到合适的位置。
最终完成循环,跳出后,j依旧会发生一次减一,所以最终的程序是要给下标为j+1的元素进行重新赋值。
之后我们再看一次整个过程,验证一下后程序:
具体操作:
print(&Data);
InsertSort(&Data);
qDebug()<<"简单的插入排序后";
print(&Data);
外循环主要是处理无序元素的遍历
内循环主要是比较要插入元素在有序队列中的位置
2希尔排序
希尔排序的思路非常类似于插入排序,但是又很有跳跃性
void ShellSort(SqList * L)
{
int i,j;
int increment = L->length;
do
{
increment = increment/3+1;
for(i = increment+1;i<=L->length;++i)
{
if(L->r[i]<L->r[i-increment])
{
L->r[0] = L->r[i];
for(j=i-increment;j>0&&L->r[0]<L->r[j];j-=increment)
L->r[j+increment] = L->r[j];
L->r[j+increment] = L->r[0];
}
}
}while(increment>1);
}
从程序的大致结构上也可以看出,和插入排序接近,但是加入了增量increment,所以整个过程也变得繁琐,
可以将上述的increment理解成插入排序中的两个元素之间的间距,插入排序是1,此处是increment
下面是两种解释方法:
(以上图源:)
以下动画演示源自:
动画图解:十大经典排序算法动画与解析,看我就够了!(配代码完全版):
https://blog.csdn.net/kexuanxiu1163/article/details/103051357