C#数据结构与算法揭秘17
这节我们介绍直接插入排序和希尔排序算法,
一、直接插入排序
直接插入排序(direct Insert Sort)的基本思想是:顺序地将待排序的记录按其关键码的大小插入到已排序的记录子序列的适当位置。 子序列的记录个数从1开始逐渐增大,当子序列的记录个数与顺序表中的记录个数相同时排序完毕。
设待排序的顺序表 sqList 中有 n 个记录,初始时子序列中只有一个记录sqList[0]。第一次排序时,准备把记录 sqList[1]插入到已排好序的子序列中,这时只需要比较 sqList[0]和 sqList[1]的大小,若 sqList[0]≤sqList[1],说明序列已有序,否则将 sqList[1]插入到 sqList[0]的前面,这样子序列的大小增大为 2。第二次排序时,准备把记录 sqList[2]插入到已排好序的子序列中,这需要先比较 sqList[2] 和 sqList[1]以确定是否需要把 sqList[2]插入到sqList[1]之前。如果 sqList[2]插入到 sqList[1]之前,再比较 sqList[2]和sqList[0]以确定是否需要把 sqList[2]插入到 sqList[0]之前。 这样的过程一直进行到 sqList[n-1]插入到子序列中为止。这时,顺序表 sqList 就是有序的。如图所示:
直接插入排序的算法如下所示,算法中记录的比较表示记录关键码的比较,顺序表中只存放了记录的关键码:相应源代码如下:
//排序的插入排序的算法 public void InsertSort(SeqList<int> sqList) { // 从头开始遍历 for (int i = 1; i < sqList.Last; ++i) { if (sqList[i] < sqList[i - 1]) { //取数值较小的 int tmp = sqList[i]; //取首先的值 int j = 0; for (j = i - 1; j >= 0&&tmp<sqList[j]; --j) { sqList[j + 1] = sqList[j]; } //进行赋值 sqList[j + 1] = tmp; } } } //双层循环,时间的复杂度是O(n2)
(1) 最好的情况是顺序表中的记录已全部排好序。这时外层循环的次数为n-1,内层循环的次数为 0。这样,外层循环中每次记录的比较次数为 1,所以直接插入排序算法在最好情况下的时间复杂度为 O(n)。
(2) 最坏情况是顺序表中记录是反序的。这时内层循环的循环系数每次均为 i。因此,直接插入排序算法在最坏情况下的时间复杂度为O(n2)。
(3) 如果顺序表中的记录的排列是随机的,则记录的期望比较次数为n2/4。因此,直接插入排序算法在一般情况下的时间复杂度为O(n2)。 可以证明,顺序表中的记录越接近于有序,直接插入排序算法的时间效率越高,其时间效率在O(n)到O(n2)之间。
直接插入排序算法的空间复杂度为 O(1)。因此,直接插入排序算法是一种稳定的排序算法。什么是稳定的排序算法,就是前面与后面的数字相等的话,不用交换。
二、希尔排序
希尔排序又叫缩小增量法,属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序。排序过程:先取一个正整数d1<n,把所有序号相隔d1的数组元素放一组,组内进行直接插入排序;然后取d2<d1,重复上述分组和排序操作;直至di=1,即所有记录放进一个组中排序为止。
/// <summary> /// Shell Sort /// </summary> /// <typeparam name="T"></typeparam> /// <param name="array"></param> public static void ShellSort<T>(T[] array) where T : IComparable { //长度 int length = array.Length; for (int h = length / 2; h > 0; h = h / 2) { //here is insert sort for (int i = h; i < length; i++) { T temp = array[i]; // 进行 渐变的 变量比较 if (temp.CompareTo(array[i -h]) < 0) { for (int j = 0; j < i; j += h) { //变量交换 if (temp.CompareTo(array[j]) < 0) { temp = array[j]; array[j] = array[i]; array[i] = temp; } } } } } }
//算法的时间复杂度是O(n2)