1. 希尔排序
希尔(Shell)排序又称为缩小增量排序,它是一种插入排序。它是直接插入排序算法的一种威力加强版。主要是为了解决插入排序中,在集合前面有序元素过多时导致的重复无用循环的问题。
由于属于插入排序的变种,故基本原理也是插入排序的原理,将整个集合分为有序以及无无序两个部分,然后从无序部分中不断取出元素放入有序集合部分。
但是希尔排序引入了一个叫步长(gap)的概念,本质是将集合分组。
希尔排序是不稳定的,即两个相同的元素相对位置,在排序后可能会发生变化。
如图:
步骤分析
-
将长度为10的集合分为 10/2 = 5 组,这五组各自有两个元素。分别对每个组内的这两个元素进行插入排序。
在这一步之后 ,若集合本身前面是有序的,只是后面有些无序序列时,此时的无序序列也已经会被前移到前半部分了
-
继续缩小步长(缩小一倍),再次分批进行插入排序。直至步长为1,再次进行最后一次插入排序,可以有效降低插入排序的无效次数
JAVA代码实现:
/**
* 希尔排序(插入排序变种)
* <p>
* 为了改善插入排序的一些弊端。比如原数组本来在前面就有很多的有序元素,插入排序前面的很多遍历就都成为了徒劳,对程序的浪费
* 希尔排序会将集合先进行分组,先按每组来排序,然后减小分组,继续进行插入排序。直至分组数小于0
* [4, 2, 17, 3, 85, 0, 31, 2, 7, 61, 43, 87, 31, 39, 38, 42, 72, 24, 20, 55]
* 20个元素,首先分10组,其中,下标位0,10是一组,1,11 是一组,2,12是一组,。。。
* 第二次,分5组,下表0,5,10,15为一组,1,6,11,16是一组。。。。
* 第三次,分2组,下表0,2,4,6,8,10,12,14,16,18为一组,1,3,5,7,9,11,13,15,17,19 为一组
* 最后分组数为1,进行整体插入排序
*
* @param arr
*/
public static void shellOrder(int[] arr) {
int buc = arr.length / 2;
while (buc > 0) {
for (int x = buc; x < arr.length; x++) {
int cur = arr[x];
int y = x - buc;
for (; y >= 0; y -= buc) {
if (arr[y] > cur) {
int temp = arr[y];
arr[y+buc] = temp;
arr[y] = cur;
}else {
break;
}
}
arr[y+buc] = cur;
}
buc = buc / 2;
}
}
下面是插入排序的算法。可以和希尔排序比较一下,就会发现其实本质就是多了个分组的步骤
/**
* 插入排序
* <p>
* 对欲排序的元素,以插入的方式寻找其合适的位置。
* 个人理解:插入排序其实就是选择排序的变种。选择排序是先选择合适的元素,放在当前位置。
* 而插入排序,则是直接找当前位置的元素,插入到顺序表合适的位置
*
* @param arr
*/
public static void insertOrder(int[] arr) {
//省略判断数组有效性步骤
for (int x = 1; x < arr.length; x++) {
int cur = arr[x];
int y = x-1;
for (; y >=0; y--) {
if (arr[y] > cur) {
arr[y+1] = arr[y];
}else {
break;
}
}
arr[y+1]=cur;
}
}
2. 归并排序
归并排序讲究的是一个分而治之的策略。面对一个无序杂乱的集合,归并排序的做法是不断地将其分割成为一个个零散的小集合,再将每个小集合依次排序组合。
图示:
可以看出来,归并算法大致分为分,与治两个大步骤。
分:就是将集合不断两两细分,直到分割成不可分割的最小元素
治:将分割的元素,按照顺序再组合成一个集合
由此可以知道,归并排序的分和治是分开的。并且归并排序是稳定的相同大小的元素,在排序前后的相对顺序不会发生改变。
/**
* 归并排序
* @param arr
*/
public void mergeOrder(int[] arr){
//使用递归进行分
radixOrder(arr,0,arr.length-1,new int[arr.length]);
}
private static void radixOrder(int[] arr,int left,int right,int []temp){
if (left<right){
int mid = (left+right)/2;
radixOrder(arr,left,mid,temp);
radixOrder(arr,mid+1,right,temp);
merge(arr,left,right,mid,temp);
}
}
private static void merge(int[] arr,int left,int right,int mid,int[] temp){
int lstart = left;
int rstart = mid+1;
int t =0;
while (lstart<=mid&&rstart<=right){
if (arr[lstart]<arr[rstart]){
temp[t++] = arr[lstart++];
}else {
temp[t++] = arr[rstart++];
}
}
while (lstart<=mid){
temp[t++] = arr[lstart++];
}
while (rstart<=right){
temp[t++] = arr[rstart++];
}
t=0;
while (left<=right){
arr[left++] = temp[t++];
}
}