希尔排序的Java实现及简单效率测试

准备工作:

希尔排序一般是在插入排序的算法上实现的,如果对此还不熟悉的小伙伴可以先去了解一下插入排序,这样学习希尔排序可以事半功倍。

正文:

希尔排序又叫缩小增量排序,具体怎么定义的,这里就不再赘述了。先举个例子,比如,有一个待排序的数组的长度为len,先取n = len / 2,把数组划分为n个子序列,每隔n个位置的元素为同一个子序列的元素,先对每个子序列的元素进行排序,效率会快出不少,然后再取n = n / 2,重复循环,知道n = 1,此时只有一个序列了,但是由于之前已经将大部分元素排序好了,所以并不会有太大的效率问题。其中,n就是增量,每次循环,n缩小一半,就是所谓的缩小增量。文字说明永远是抽象的,上图:

这是一个待排序的数组,len = 8,先取n = len / 2 = 4,将数组划分为4个子序列,每隔4个位置就是同一子序列中的元素(同种颜色的元素为同一子序列中的)

这是第一次排序后的结果,可以看到,每个子序列的元素都是有序的了,然后再取 n = n / 2 = 2,将整个数组划分为2个子序列,再进行排序

这是第二次排序后的结果,同样的道理,每个子序列中的元素都是有序的,随后n = 1,也就是最后的排序了

如果上面的思维理清了,就可以直接上代码了

package sort;

import java.util.Arrays;

public class SortTest {
    public static void main(String[] args) {
        int[] arr = {12, 48, 21, 65, 2, 1, 92, 37};
        shellSort(arr);
    }

    private static void shellSort(int[] arr) {
        int n = arr.length;
        int i, len = n / 2;
        int count = 1;
        while (len >= 1) {
            for (i = len; i < n; i++) {//从当前增量位置开始往后循环,往前每隔len个就是同一个子序列的元素
                if (arr[i] < arr[i - len]) {//前面已经有序,若最后一个比a[i]还小,说明a[i]已经在合适的位置了,大大减小循环量
                    //在排序好的子序列里找位置
                    int temp = arr[i];//a[i]在while第一次循环值就会改变,所以要用临时变量记录下来,因为之后在子序列里给他找位置肯定是和他比较
                    int k = i - len;
                    while (k >= 0 && arr[k] > temp) {
                        //已经排序好的子序列肯定是有序的,这里只是给a[i]找到合适的位置,每次比a[i]大的元素向后移动一个位置,然后把这个位置留给a[i]
                        arr[k + len] = arr[k];//这里就是向后移动一个位置
                        k -= len;//然后下标向前移动,判断是否还是比a[i]大,是的话就继续后移
                    }
                    arr[k + len] = temp;//最后把a[i]放到合适的位置
                }
            }
            System.out.println("第" + count++ + "趟排序,结果为" + Arrays.toString(arr));
            len /= 2;
        }
    }
}

到此,希尔排序的实现已经结束了,那么它的效率到底怎么样呢,这里就简单地和冒泡排序比较一下

package sort;

import java.util.Random;

public class SortTest {
    public static void main(String[] args) {
        int[] arr = new int[200000];
        int[] brr = new int[200000];
        Random random = new Random();
        for (int i = 0; i < 200000; i++) {
            arr[i] = random.nextInt(1000);
            brr[i] = random.nextInt(1000);
        }
        shellSort(arr);
        bubbleSort(brr);
    }

    private static void shellSort(int[] a) {
        long start = System.currentTimeMillis();
        int n = a.length;
        int i, len = n / 2;
        while (len >= 1) {
            for (i = len; i < n; i++) {//从当前增量位置开始往后循环,往前每隔len个就是同一个子序列的元素
                if (a[i] < a[i - len]) {//前面已经有序,若最后一个比a[i]还小,说明a[i]已经在合适的位置了,大大减小循环量
                    //在排序好的子序列里找位置
                    int temp = a[i];//a[i]在while第一次循环值就会改变,所以要用临时变量记录下来,因为之后在子序列里给他找位置肯定是和他比较
                    int k = i - len;
                    while (k >= 0 && a[k] > temp) {
                        //已经排序好的子序列肯定是有序的,这里只是给a[i]找到合适的位置,每次比a[i]大的元素向后移动一个位置,然后把这个位置留给a[i]
                        a[k + len] = a[k];//这里就是向后移动一个位置
                        k -= len;//然后下标向前移动,判断是否还是比a[i]大,是的话就继续后移
                    }
                    a[k + len] = temp;//最后把a[i]放到合适的位置
                }
            }
            len /= 2;
        }
        long end = System.currentTimeMillis();
        System.out.println("希尔插入排序完成的时间:" + (end - start) + "毫秒");
    }

    private static void bubbleSort(int[] arr) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        System.out.println("冒泡排序完成的时间:" + (System.currentTimeMillis() - start) + "毫秒");
    }
}

两个数组都是20w相同的随机数进行排序,其结果:

效率整整相差了1800多倍,可怕!

猜你喜欢

转载自blog.csdn.net/weixin_39561657/article/details/88774482