插入排序及哨兵的作用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Q_AN1314/article/details/72050453

0插入排序的做法是每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。与选择排序一样,当前索引的左边的元素是有序的,但它们的最终位置还不确定,为了给更小的元素腾出空间,它们可能会被移动。当索引到达数组的右端时,排序完成。

public class Sort {

    private static void swap(Comparable[] a, int i, int j) {
        Comparable temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

    public static void insertionSort(Comparable[] a) {
        int n = a.length;
        for (int i = 1; i < n; i++) {
            for (int j = i; j > 0 && a[j].compareTo(a[j-1]) < 0 ; j--) {
                swap(a, j-1, j);
            }
        }
    }

    public static void insertSortWithSentinel(Comparable[] a) {
        //n比较大的时候用哨兵可以节省一个边界条件判定的耗时
        int n = a.length;

        //先找出最小的元素并放到数组最左边,以为哨兵
        int min = 0;
        for (int i = 0; i < n; i++) {
            if (a[min].compareTo(a[i]) > 0) {
                min = i;
            }
        }
        swap(a, min, 0);

        for (int i = 1; i < n; i++) {
            if (a[i].compareTo(a[i-1]) < 0) {
                for (int j = i; a[j].compareTo(a[j-1]) < 0; j--) { //可以看到比insertionSort函数的对应部分少了一个j>0这个判定条件,这就是哨兵的作用
                    swap(a, j, j-1);
                }
            }
        }
    }

用随机生成的长度为10000的100个数组做测试:

public class SortCompare {

    public static double time(String alg, Double[] a) {
        StopWatch timer = new StopWatch();
        if (alg.equals("Insertion")) Sort.insertionSort(a);
        if (alg.equals("Selection")) Sort.selectionSort(a);
        if (alg.equals("Shell")) Sort.shellSort(a);
        if (alg.equals("InsertionWithSentinel")) Sort.insertSortWithSentinel(a);
        //if (alg.)
        return timer.elapsedTime();
    }

    public static double timeRandomInput(String alg, int n, int t) {
        //使用算法alg将t个长度为n的数组排序
        double total = 0.0;
        Double[] a = new Double[n];
        for (int i = 0; i < t; i++) {
            //进行一次测试(生成一个数组并排序)
            for (int j = 0; j < n; j++) {
                a[j] = StdRandom.uniform();
            }
            total += time(alg, a);
        }
        return total;
    }

    public static void main(String[] args) {
        double t1 = timeRandomInput("Insertion", 10000, 100);
        double t2 = timeRandomInput("InsertionWithSentinel", 10000, 100);
        System.out.println(t1 + " " + t2);
    }
}

结果是:1.4792608107E10 1.2151381248E10,单位是纳秒。所以可以看出哨兵在数组长度比较大的时候是有用的,只用了一个O(N)的操作就规避了一个边界测试(操作大于O(N))。

展开阅读全文

没有更多推荐了,返回首页