1、简单插入排序存在的问题

我们看简单的插入排序可能存在的问题.
数组 arr = {2,3,4,5,6,1} 这时需要插入的数 1(最小), 这样的过程是:
{2,3,4,5,6,6}
{2,3,4,5,5,6}
{2,3,4,4,5,6}
{2,3,3,4,5,6}
{2,2,3,4,5,6}
{1,2,3,4,5,6}
结论: 当需要插入的数是较小的数时,后移的次数明显增多,对效率有影响.
**

2、希尔排序法介绍

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。
**

3、希尔排序法基本思想

希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止

4、希尔排序法 的示意图


图片.png
图片.png**

5、希尔排序的两种方法

5.1、交换法

5.1.1、源码

  1. package com.study.sort;
  2. import java.util.Arrays;
  3. import java.util.Random;
  4. public class ShellSort {
  5. public static void main(String[] args) {
  6. int[] arr ={8,9,1,7,2,3,5,4,6,0};
  7. shellSort(arr);
  8. }
  9. //希尔排序:交换法
  10. public static void shellSort(int[] arr){
  11. int count = 1;//计算排序次数
  12. int temp = 0;//定义临时变量
  13. for (int gorp = arr.length / 2; gorp > 0; gorp /= 2){//将数组不断分组(arr.length / 2)循环
  14. for (int i = gorp; i < arr.length; i++){
  15. for (int j = i-gorp; j >= 0; j -= gorp){//遍历当前分组,一共gorp个组,步长为gorp;
  16. //如果当前元素大于加上步长后的那个元素,则交换
  17. if (arr[j] > arr[j+gorp]){
  18. temp = arr[j];
  19. arr[j] = arr[j+gorp];
  20. arr[j+gorp] = temp;
  21. }else {
  22. break;
  23. }
  24. }
  25. }
  26. System.out.println("第"+(count++)+"轮排序:"+ Arrays.toString(arr));
  27. }
  28. }

5.1.2、运行结果

图片.png

5.1.3、测试时间

  1. package com.study.sort;
  2. import java.util.Arrays;
  3. import java.util.Random;
  4. public class ShellSort {
  5. public static void main(String[] args) {
  6. int[] arr = new int[8000000];
  7. Random random = new Random();
  8. for (int i = 0; i < 8000000; i++) {
  9. arr[i] = random.nextInt(800000000);
  10. }
  11. //交换法时间:
  12. long l1 = System.currentTimeMillis();
  13. shellSort(arr);
  14. long l2 = System.currentTimeMillis();
  15. System.out.println("希尔排序(交换法)对"+arr.length+"个数据排序所花费的时间为:"+(l2-l1)+"ms");
  16. }
  17. //希尔排序:交换法
  18. public static void shellSort(int[] arr){
  19. int count = 1;//计算排序次数
  20. int temp = 0;//定义临时变量
  21. for (int gorp = arr.length / 2; gorp > 0; gorp /= 2){//将数组不断分组(arr.length / 2)循环
  22. for (int i = gorp; i < arr.length; i++){
  23. for (int j = i-gorp; j >= 0; j -= gorp){//遍历当前分组,一共gorp个组,步长为gorp;
  24. //如果当前元素大于加上步长后的那个元素,则交换
  25. if (arr[j] > arr[j+gorp]){
  26. temp = arr[j];
  27. arr[j] = arr[j+gorp];
  28. arr[j+gorp] = temp;
  29. }else {
  30. break;
  31. }
  32. }
  33. }
  34. //System.out.println("第"+(count++)+"轮排序:"+ Arrays.toString(arr));
  35. }
  36. }

测试结果:
希尔排序(交换法)对80000个随机数进行排序,只用了20ms,比之前的插入排序快了很多,其实希尔排序(交换法)只是在冒泡排序的基础上进行不断地分组,我们之前测试冒泡排序所用的时间为11s。
如果我们以插入排序为基础,进行希尔排序,那么时间应该会更短?
图片.png

5.2、移位法

5.2.1、源码

  1. package com.study.sort;
  2. import java.util.Arrays;
  3. import java.util.Random;
  4. public class ShellSort {
  5. public static void main(String[] args) {
  6. int[] arr ={8,9,1,7,2,3,5,4,6,0};
  7. shellSort2(arr);
  8. }
  9. //希尔排序:移位法
  10. public static void shellSort2(int[] arr){
  11. int count = 1;//计算排序次数
  12. for (int gorp = arr.length / 2;gorp>0; gorp /= 2){
  13. for (int i = gorp;i < arr.length;i++){
  14. int j = i;
  15. int temp = arr[j];
  16. if (arr[j] < arr[j-gorp]){
  17. while (j-gorp >= 0 && temp < arr[j-gorp]){
  18. arr[j] = arr[j-gorp];
  19. j -= gorp;
  20. }
  21. arr[j] = temp;
  22. }
  23. }
  24. System.out.println("第"+(count++)+"轮排序:"+ Arrays.toString(arr));
  25. }
  26. }
  27. }

5.2.2、运行结果

图片.png

5.2.3、测试时间

  1. package com.study.sort;
  2. import java.util.Arrays;
  3. import java.util.Random;
  4. public class ShellSort {
  5. public static void main(String[] args) {
  6. /* int[] arr ={8,9,1,7,2,3,5,4,6,0};
  7. shellSort2(arr);*/
  8. int[] arr = new int[80000];
  9. Random random = new Random();
  10. for (int i = 0; i < 80000; i++) {
  11. arr[i] = random.nextInt(800000000);
  12. }
  13. //交换法时间:
  14. long l1 = System.currentTimeMillis();
  15. shellSort(arr);
  16. long l2 = System.currentTimeMillis();
  17. System.out.println("希尔排序(交换法)对"+arr.length+"个数据排序所花费的时间为:"+(l2-l1)+"ms");
  18. //移位法时间:
  19. long l11 = System.currentTimeMillis();
  20. shellSort2(arr);
  21. long l22 = System.currentTimeMillis();
  22. System.out.println("希尔排序(移位法)对"+arr.length+"个数据排序所花费的时间为:"+(l22-l11)+"ms");
  23. }
  24. //希尔排序:交换法
  25. public static void shellSort(int[] arr){
  26. int count = 1;//计算排序次数
  27. int temp = 0;//定义临时变量
  28. for (int gorp = arr.length / 2; gorp > 0; gorp /= 2){//将数组不断分组(arr.length / 2)循环
  29. for (int i = gorp; i < arr.length; i++){
  30. for (int j = i-gorp; j >= 0; j -= gorp){//遍历当前分组,一共gorp个组,步长为gorp;
  31. //如果当前元素大于加上步长后的那个元素,则交换
  32. if (arr[j] > arr[j+gorp]){
  33. temp = arr[j];
  34. arr[j] = arr[j+gorp];
  35. arr[j+gorp] = temp;
  36. }else {
  37. break;
  38. }
  39. }
  40. }
  41. //System.out.println("第"+(count++)+"轮排序:"+ Arrays.toString(arr));
  42. }
  43. }
  44. //希尔排序:移位法
  45. public static void shellSort2(int[] arr){
  46. int count = 1;//计算排序次数
  47. for (int gorp = arr.length / 2;gorp>0; gorp /= 2){
  48. for (int i = gorp;i < arr.length;i++){
  49. int j = i;
  50. int temp = arr[j];
  51. if (arr[j] < arr[j-gorp]){
  52. while (j-gorp >= 0 && temp < arr[j-gorp]){
  53. arr[j] = arr[j-gorp];
  54. j -= gorp;
  55. }
  56. arr[j] = temp;
  57. }
  58. }
  59. //System.out.println("第"+(count++)+"轮排序:"+ Arrays.toString(arr));
  60. }
  61. }
  62. }

图片.png
测试结果我们发现移位法比交换法还要快,80000个随机数只用短短的4ms就完成了排序,其实不难理解,之前测试的插入排序和冒泡排序所用时间为852ms和11s,很明显插入排序比冒泡排序快很多,所以在插入排序的基础上嵌套一层希尔排序后速度将起飞,嘿嘿。。

再看看800w个随机数所用的时间,如果理论上只要某算法的排序时间比插入排序还短,那么进行希尔排序后,时间可能还会更短
图片.png
**许这就是算法的魅力吧
**