文章目录
1 插入排序
1.1 基本思想
把待排序的,插入到已经排好序的合适的位置。
边插入边排序,保证子序列都是排好序的。
1.2 基本操作
1.3 插入位置
Q:如何找到插入位置?
A:下方的插入排序方法将会介绍。
2 插入排序的种类
2.1 直接插入排序
采用顺序查找法查找插入位置。
循环跳出条件为:j >= 0 && x < a[j]
从后往前找,但有个问题,每次循环都要判断两个,时间太久,之前在顺序查找法的时候,有个带哨兵的,现在可以用上。直接把待插入元素第i个位置上的元素放到下标为0的位置(哨兵位置)上面。
2.1.1 直接插入排序算法
void InsertSort(SqList &L)
{
int i, j;
for (i = 2; i <= L.length(); ++i)
{
if (L.r[i].key < L.r[i-1].key)//若"<",需要将L.r[i]插入到有序子表
{
L.r[0] = L.r[i];//复制2为哨兵,减少循环的比较条件
for (j = i-1; L.r[0].key < L.r[j].key; --j)
{
L.r[j+1] = L.r[j];//记录后移
}
L.r[j+1] = L.r[0];//插入到正确的位置
}
}
}
2.1.2 性能分析
2.1.3 结论
2.2 折半插入排序
直接插入法是在查找位置的时候,从后往前依次进行比较的,这种一个一个进行比较的方法叫做顺序查找法。折半查找法定义一个low端一个high端,找到mid中间位置,与哨兵位置比较,mid比哨兵小,就在右半区域进行查找;反之亦然。找到后依然是要将位置向后移动,然后将哨兵位置的元素放到合适的位置。
2.2.1 折半插入排序算法
void BInsertSort(SqList &L)
{
for (i = 2; i <= L.length(); ++i)//依次插入第2~第n个位置
{
L.r[0] = L.r[i];//当前插入元素存到哨兵位置
low = 1;
high = i-1;//采用二分查找法查找插入位置
while(low <= high)
{
mid = (low + high) / 2;
if (L.r[0].key < L.r[mid].key)
{
high = mid - 1;
}
else
{
low = mid + 1;
}
}//循环结束,high+1则为插入位置
for (j = i-1; j >= high+1; --j)
{
L.r[j+1] = L.r[j];//移动元素
}
L.r[high+1] = L.r[0];//插入到正确的位置
}
}
2.2.2 性能分析
折半只是减少了比较的次数,移动的次数没有减少。
2.3 希尔排序
希尔排序算法的出发点,就是怎么能够在进行插入排序的时候基本有序,怎么能比较一次就移动一大步,相当于是继承了上方几种排序的优点。
基本思想:
先将整个待排记录序列分割成若干子序列(元素个数就少了,交换的时候可以移动一大步),分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
希尔排序算法,特点:
- 缩小增量
- 多遍插入排序
思路:
最后一次是1间隔的插入排序。
特点:
2.3.1 希尔排序算法
//主程序
void ShellSort(SqList &L, int dlta[], int t)
{
//按增量序列dlta[0...t-1]对顺序表L作希尔排序
for (k=0; k<t; ++k)
{
ShellInsert(L, dlta[k]);//一趟增量为dlta[k]得到插入排序,总共是循环的t趟
}
}
//其中一趟的排序操作
void ShellInsert(SqList &L, int dk)
{
for (i=dk+1; i<=L.length(); ++i)
{
if (r[i].key < r[i-dk].key)
{
r[0] = r[i];
for (j=i-dk; j>0 && (r[0].key<r[j].key);j=j-dk)
{
r[j+dk]=r[j];
}
r[j+dk] = r[0];
}
}
}
本质上还是插入排序,但是按照间隔来的。
2.3.2 性能分析
非稳定性算法