前言
直接插入在本质上改良了之前的冒泡和选择排序,而希尔排序更是超越O(n^2)的传统排序的改良算法
直接插入排序
算法描述:直接插入排序就是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表
首先先定义数据结构
#define MAXSIZE 10
typedef struct{
int r[Maxsize+1]//用于存储要排序的数组,r[0]用作哨兵或者临时变量
int length; //记录顺序表的长度
}SqList;
接下来不妨以5个数模拟该算法执行过程
初始化为{0,5,3,4,6,2}
从第二个数开始往前比较,如3比5小,那么,3赋值给哨兵,5后移到3位置,哨兵赋值给5位置,此时变成
{3,3,5,4,6,2};
继续循环,哨兵初始化为0,4比5小,4给哨兵,5后移至4,在比较哨兵与3大小,如果比3小,3后移,在插入到该位置,否则直接插到5位置,ok这就是该算法的核心,在合适的位置直接插入
码上有戏
下面在来看算法,一般理解算法的过程,就把自己当做计算机,自我调试,然后就会发现其中无穷的乐趣
下面在看看它的时间复杂度,最好的情况就是{2,3,4,5,6},也就是说其本身已经是一个有序的,很显然只需要L->r[i]与L->r[r-1]大小,所以此时时间复杂度为O(n);最坏的情况就是{6,5,4,3,2},即2+3+。。+n为(n+2)(n-1)/2
所以最后根据平局原则,知道直接插入排序的时间复杂度为O(n^2);
希尔排序
基本有序
首先需要了解一下基本有序的概念,就是指小的关键字基本在前面,大的基本在后面,不大不小的基本在中间,如{2,1,3,6,4,7,5,8,9},但如{1,5,9,3,7,8,2,4,6}这种9在第三位,2在倒数第三位就不叫基本有序
希尔排序的关键就是将相隔某个”增量”的记录组成一个子序列,实现跳跃式的移动
初始一个数组{0,9,1,5,8,3,7,4,6,2},其中0为哨兵
并且初始化增量为数组长度(哨兵不算),所以为9,并约定一个循环,当且仅当增量小于等于1退出循环,并且每次增量都执行增量=增量/3+1(也可以是其他增量原则),第一次就是4了
然后一个循环变量i从增量+1开始直到小于等于数组长度,然后比较该i值与i-增量值,也就是第一个值大小,后面就是直接插入算法思想了,有一点不一样,就是循环变量j控制不太一样
然后看看上述数组怎么比较的
第一次:i=增量+1=5开始,也就是3与9比较,明显满足3<9,然后交换(直接插入思想),依次7与1,不交换,4与5交换,6与8交换,2与9交换这里那个j条件也满足并且2<3在交换,ok得到一个交换后的数组
{2,1,4,6,3,7,5,8,9}可以发现经过第一轮后,该数组基本有序了
紧接着开始进行第二轮,即增量=增量/3+1=2,即从第三个2与4不交换,1与6不交换,4与3交换,6与7不交换,4与5不交换,7与8不交换,5与9不交换,即得到{2,1,3,6,4,7,5,,8,9};
最后一轮:增量为1,2与1交换,2与4不交换,然后依次比较,由于目前已经基本有序,所以交换的比较少,
最后得到{1,2,3,4,5,6,7,8,9}
码上有戏
通过上面的例子,大概就是希尔排序的模拟过程,可以发现增量值是一个很巧妙的地方
可以发现希尔排序的关键就是增量的选择,而它的时间复杂度是O(n^(3/2)),显然比传统的算法O(n^2)时间复杂度要小很多