1. 插入排序
直接上图
说明
1.黑色竖线将数据(data)分割为两个部分,对竖线左边进行排序
2.蓝色为已近排序的元素,红色没有排序
1.1插入排序算法流程
第一次排序: j=1, i =0;.其实就两个元素,比较一下,data[j] < data[i]. 交换二者
第二次排序:前两个元素已经有序。扩张这个有序序列.于是我们需要找到第 i 个元素应该放在哪里?
这里我们将i以此向前比较,发现,它比最大的23都打,好吧,那就放在23后面不动吧。
第三次排序:还是找 i 个元素12应该放在哪里?
首先我们应该把12用一个temp变量保存下来
然后,发现24比12大,那就把24往后挪到12的位置,然后在依次把14 -->24, 23–>14。最后,把12放到停下的位置。
1.2插入排序代码
void insert_sort(int *data, int length) {
int i, j;
for (i = 1; i < length; i++) //i依次往后遍历
{
int tmp = data[i];
for ( j = i-1; j>=0; j--)//j 表示 i 前一个元素
{
if(data[j]>tmp){
//如果j个元素已经比前一个元素大,说明应该插入到这个元素后面,因为前面的元素会更小
break;
}
//往后挪动
data[j + 1] = data[j];
}
//把temp的值,放到j+1个位置
data[j + 1] = tmp;
}
}
2.希尔排序
2.1原理
希尔排序可以理解为插入排序的升级版,插入排序是相邻位置比较,希尔排序 跨元素比较,如上图中的,开始 data[0] 和 data[0+gap] 位置的元素比较。 即data[i] 与 data[i+gap]比较。i依次递增。
希尔排序其实是做0, 0+gap, 1+ gap, 2+gap, 3+gap…位置的插入排序。一轮结束过后, gap 除以2. 直到gap为1.
(gap为1, 就是插入排序啦。相邻位置挨个比较,达到全局有序)。
总结一下:
- 先对0,0+gap,1+gap,3+gap 间隔gap个元素做插入排序。
- gap = gap/2;
- while(gap>=1)
2.2代码
void shell_sort(int *data, int length) {
int gap = 0;
int i = 0, j = 0;
for (gap = length/2; gap>=1; gap = gap/2)
{
// 当 gap =1 的时候,就是插入排序。
for ( i = gap; i < length; i++)
{
int tmp = data[i];
for (j = i-gap; j>=0; j=j-gap )
{
if(data[j]>tmp) break;
data[j + gap] = data[j];
}
data[j + gap] = tmp;
}
}
}
时间复杂度:插入排序 O(nn)最好的情况O(n) 最坏情况(nn)
希尔排序 O(n^1.3) 最好的情况O(n) 最坏的情况(n*n)
空间复杂度:都是O(1)
稳定性: 插入排序是稳定的,希尔排序不稳定。
啥是稳定性?
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。(摘自百度百科)
我们以希尔排序为例,假设这是一个排序的中间状态。 我们可以发现 在这一个状态下, 9 是排在 16前面的,但是在下一个状态,9 排在了 16 后面。那原本有序的 变成了 逆序对。可以看出希尔排序是不稳定的。同样的,我们看插入排序
12 不管怎么移动, 所有元素的相对大小位置都不会改变,这就说明,插入排序是稳定的。
排序算法的稳定性,应该具体情况具体讨论,不可一概而论。(摘自百度百科)
一般稳定的排序算法有:
而基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法。