习题答案
题目回顾
在上一篇文章中,我们以数列从小到大排列为例,讲了插入排序。结尾处的思考题如下:
如果要实现从大到小排列,上述代码该做如何修改呢?
同样,要解答这个问题也很简单,下面放上答案。
答案
我们知道,从小到大排序时,实际上就是逐个比较大小,把更小的元素向前移。同时,把整个数组分为“已排序”和“未排序”两个区域。要解决从大到小排序时,具体做法类似,只需将更大的元素向前移即可。参考如下代码:
public static void insertSort(int[] arr) { int temp; int j; for (int i = 1; i < arr.length; i++) { for (j = i - 1; j >= 0; j--) { if (arr[i] < arr[j]) break; } if (j != i - 1) { temp = arr[i]; for (int k = i; k > j + 1; k--) { arr[k] = arr[k - 1]; } arr[j + 1] = temp; } } }
怎么样,你答对了吗?
本篇文章的内容是讲第四种排序方法——希尔排序。
还是之前的问题:
问题挑战
现有如下数字:
3,44,38,5,47,15,36,26,27,2,46,4,19,50,48
一共15个数字,请将其从小到大依次排列。
算法解析
希尔排序,又被称为缩小增量排序。它和插入排序有相同之处,也有所区别——希尔排序会优先比较距离较远的元素。
概括地讲,希尔排序就是先取一个正整数,这个正整数暂定为3。把所有序号相隔这个数值的数组元素放一组,组内进行直接插入排序;然后再把这个正整数做自减一运算,重复上述分组和排序操作;直至这个正整数的值为1,即所有记录放进一个组中排序为止。
为什么我们把步长(即上文中的“正整数”)初始值定位3呢?因为该排序算法最复杂的就是该步长的确定。按照自减一再排序的操作,整个排序过程只需要3次排序即可完成。
是不是觉得有点不太好理解?下面我们分步拆解:
详细步骤
下面让我们来分步骤拆解整个希尔排序:
- 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
- 按增量序列个数 k,对序列进行 k 趟排序;
- 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
希尔排序的思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录基本有序时,再对全体记录进行依次直接插入排序。整个流程如下图(以增量为3为例)所示:
伪代码
接下来,我们使用伪代码实现上述过程
input: an array a of length n with array elements numbered 0 to n − 1 inc ← round(3) while inc > 0 do: for i = inc .. n − 1 do: temp ← a[i] j ← i while j ≥ inc and a[j − inc] > temp do: a[j] ← a[j − inc] j ← j − inc a[j] ← temp inc ← round(inc--)
Java代码实现
接下来,我们使用Java编程语言实现上述算法。
public static void shellSort(int[] arr) { int gap = arr.length; while (true) { // gap 表示增量,这里以数组长度的一半为例 gap /= 2; for (int i = 0; i < gap; i++) { for (int j = i + gap; j < arr.length; j += gap) { int temp = arr[j]; int k = j - gap; while (k >= 0 && arr[k] > temp) { arr[k + gap] = arr[k]; k -= gap; } arr[k + gap] = temp; } } if (gap == 1) break; } }
思考题
1. 如果要实现从大到小排列,上述代码该做如何修改呢?
思考题答案依旧会在下篇连载中公布,大家加油哦!