希尔排序
原文地址:原文
希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。
原理:
在插入排序中,当需要插入的数较小时(从小到大排序),后移的次数将显著的增加,从而对效率产生影响。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
各组内的排序通常采用直接插入法。由于开始时s的取值较大,每组内记录数较少,所以排序比较快。随着不断增大,每组内的记录数逐步增多,但由于已经按排好序,因此排序速度也比较快。
图解:
代码实现:(分析代码)
public class ShellSort {
public static void main(String[] args) {
int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
System.out.println("排序前:"+Arrays.toString(arr));
shellSort(arr);
System.out.println("排序后:"+Arrays.toString(arr));
}
private static void shellSort(int[] arr) {
/**
* 分析过程
* 一次循环
*/
//第一次排序
int tmp = 0;
for (int i = 5; i < arr.length; i++) {
//分组交换,i-5 指向第一个位置,
for (int j = i - 5; j >= 0; j -= 5) {
//j+5 指向第6个位置进行比较,因为分为5组
if (arr[j] > arr[j + 5]) {
tmp = arr[j];
arr[j] = arr[j + 5];
arr[j + 5] = tmp;
}
}
}
System.out.println("第1次分组:" + Arrays.toString(arr));
//第二次排序
for (int i = 2; i < arr.length; i++) {
//分组交换,i-5 指向第一个位置,
for (int j = i - 2; j >= 0; j -= 2) {
//j+5 指向第6个位置进行比较,因为分为5组
if (arr[j] > arr[j + 2]) {
tmp = arr[j];
arr[j] = arr[j + 2];
arr[j + 2] = tmp;
}
}
}
System.out.println("第2次分组:" + Arrays.toString(arr));
//第二次排序
for (int i = 1; i < arr.length; i++) {
//分组交换,i-5 指向第一个位置,
for (int j = i - 1; j >= 0; j -= 1) {
//j+5 指向第6个位置进行比较,因为分为5组
if (arr[j] > arr[j + 1]) {
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
System.out.println("第3次分组:" + Arrays.toString(arr));
}
}
最终代码:(交换法)
public class ShellSort {
public static void main(String[] args) {
int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
System.out.println("排序前:" + Arrays.toString(arr));
shellSort(arr);
System.out.println("排序后:" + Arrays.toString(arr));
}
private static void shellSort(int[] arr) {
int tmp = 0;
for (int group = arr.length / 2; group > 0; group /= 2) {
for (int i = group; i < arr.length; i++) {
//分组交换,i-5 指向第一个位置,
for (int j = i - group; j >= 0; j -= group) {
//j+5 指向第6个位置进行比较,因为分为5组
if (arr[j] > arr[j + group]) {
tmp = arr[j];
arr[j] = arr[j + group];
arr[j + group] = tmp;
}
}
}
}
}
}
排序前:[8, 9, 1, 7, 2, 3, 5, 4, 6, 0]
排序后:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
测试时间:
public class ShellSort {
/**
* 测试时间
*/
@Test
public void testTime() {
int[] arr = new int[80000];
for (int i = 0; i < 80000; i++) {
arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
}
System.out.println("排序前:" + Arrays.toString(arr));
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
shellSort(arr);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
System.out.println("排序后:" + Arrays.toString(arr));
}
private static void shellSort(int[] arr) {
int tmp = 0;
for (int group = arr.length / 2; group > 0; group /= 2) {
for (int i = group; i < arr.length; i++) {
//分组交换,i-5 指向第一个位置,
for (int j = i - group; j >= 0; j -= group) {
//j+5 指向第6个位置进行比较,因为分为5组
if (arr[j] > arr[j + group]) {
tmp = arr[j];
arr[j] = arr[j + group];
arr[j + group] = tmp;
}
}
}
}
}
}
排序前的时间是=2019-09-10 10:02:47
排序前的时间是=2019-09-10 10:02:53
可以发现,交换法虽然思想可以实现,但是相比直接插入排序,时间反而更加多了,这显然是不对的。
代码优化:(移位法)
优化原理:
在交换法中,是for循环中不断找是否有可以交换的,显示时间大部分都浪费掉了,于是我们采用图解以及原理所说的直接插入法。当然,直接插入是前后进行比较,移位位位1。由于希尔排序分组了,移位就变为每次分组的组数(group)。
public class ShellSort {
public static void main(String[] args) {
int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
System.out.println("排序前:" + Arrays.toString(arr));
shellSort3(arr);
System.out.println("排序后:" + Arrays.toString(arr));
}
/**
* 测试时间
*/
@Test
public void testTime() {
int[] arr = new int[80000];
for (int i = 0; i < 80000; i++) {
arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
}
System.out.println("排序前:" + Arrays.toString(arr));
Date data1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(data1);
System.out.println("排序前的时间是=" + date1Str);
shellSort3(arr);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
System.out.println("排序后:" + Arrays.toString(arr));
}
/**
* 百度百科里的希尔排序
* @param array
*/
private static void shellSort(int[] array) {
//希尔排序
int gap = array.length;
while (true) {
gap /= 2; //增量每次减半
for (int i = 0; i < gap; i++) {
for (int j = i + gap; j < array.length; j += gap) {//这个循环里其实就是一个插入排序
int temp = array[j];
int k = j - gap;
while (k >= 0 && array[k] > temp) {
array[k + gap] = array[k];
k -= gap;
}
array[k + gap] = temp;
}
}
if (gap == 1)
break;
}
}
/**
* 希尔排序(移位法)
*
* @param arr
*/
private static void shellSort3(int[] arr) {
int tmp = 0;
for (int group = arr.length / 2; group > 0; group /= 2) {
//第i词插入
for (int i = 1; i < arr.length; i++) {
//无序部分第一个值,即待插入值
int value = arr[i];
//有序部分最后一个值
int yw = i - group;
//插入位置结束条件
while (yw >= 0 && arr[yw] > value) {
//待插入值先前移动,有序部分向后移动
arr[yw + group] = arr[yw];
//让待插入值与有序部分倒数第(yw+) 比较
yw -= group;
}
//待插入值插入,因为退出循环,说明位置找到(yw+1)
arr[yw + group] = value;
}
}
}
}
排序前的时间是=2019-09-10 10:35:32
排序前的时间是=2019-09-10 10:35:32