1.7 - 排序基础 - 更多O(n^2)级别的排序算法

    对于任何一个数组,选择排序两层循环的每一层循环都必须完全的执行完成。正是因为如此,选择排序的效率在任何情况下都是非常慢的。相较之下,插入排序最差的时间复杂度也是O(n^2)级别的,但是在数组近乎有序的情况下,插入排序的性能非常高,甚至会比O(nlogn)级别的排序性能还要高,这使插入排序有非常重要的实际意义。

    另外一种排序算法:冒泡排序( BubbleSort)-- 是通常所接触的第一个排序算法。事实上,冒泡排序的性能整体没有插入排序好,不会过多使用。思考并熟悉即可。

    

    它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端。

BubbleSort

package bobo.algo;

public class BubbleSort {

    // 我们的算法类不允许产生任何实例
    private BubbleSort(){}

    public static void sort(Comparable[] arr){

        int n = arr.length;
        boolean swapped = false;

        do{
            swapped = false;
 
            for( int i = 1 ; i < n ; i ++ )
                if( arr[i-1].compareTo(arr[i]) > 0 ){
                     swap( arr , i-1 , i );
                    swapped = true;
                }
            n --;
        }while(swapped);
    }

    private static void swap(Object[] arr, int i, int j) {
        Object t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }
}

 Main  

package bobo.algo;

import java.util.Arrays;

public class Main {

    // 比较SelectionSort, InsertionSort和BubbleSort三种排序算法的性能效率
    public static void main(String[] args) {

        int N = 20000;

        // 测试1 一般测试
        System.out.println("Test for random array, size = " + N + " , random range [0, " + N + "]");

        Integer[] arr1 = SortTestHelper.generateRandomArray(N, 0, N);
        Integer[] arr2 = Arrays.copyOf(arr1, arr1.length);
        Integer[] arr3 = Arrays.copyOf(arr1, arr1.length);

        SortTestHelper.testSort("bobo.algo.SelectionSort", arr1);
        SortTestHelper.testSort("bobo.algo.InsertionSort", arr2);
        SortTestHelper.testSort("bobo.algo.BubbleSort", arr3);

        System.out.println();


        // 测试2 测试近乎有序的数组
        int swapTimes = 100;
        System.out.println("Test for nearly ordered array, size = " + N + " , swap time = " + swapTimes);

        arr1 = SortTestHelper.generateNearlyOrderedArray(N, swapTimes);
        arr2 = Arrays.copyOf(arr1, arr1.length);
        arr3 = Arrays.copyOf(arr1, arr1.length);

        SortTestHelper.testSort("bobo.algo.SelectionSort", arr1);
        SortTestHelper.testSort("bobo.algo.InsertionSort", arr2);
        SortTestHelper.testSort("bobo.algo.BubbleSort", arr3);

        return;
    }
}

      另外,通过插入排序法还可以引申出一种非常重要的排序算法:希尔排序(ShellSort)。希尔排序整体的思路就是插入排序的延伸,我们在插入排序中,是每一次和之前的一个元素进行比较。而希尔排序,是每一次和之前第h个元素进行比较,同过将一个很大的值从h逐渐缩小到1,一步一步将完全无序的数组变成近乎有序的数组,变成有序性更强的数组,最后当h=1时,最终变成一个排好序的数组,这个过程使整个算法的时间复杂度发生巨变。但是希尔排序相对整个时间复杂度的分析是比较难的,我们选择不同的从h逐渐递减的序列,它的时间复杂度也是不同的。

    了解希尔排序的排序过程,实现它:

    

    希尔排序(Shell Sort)也称缩小增量排序,是插入排序算法的一种更高效的改进版本。它是把数列按下标的一定增量分组,对每组使用插入排序算法排序;增量逐渐减少,直至减至1时,整个数列恰被分成一组,算法便终止。

ShellSort

package bobo.algo;

public class ShellSort {
    // 我们的算法类不允许产生任何实例
    private ShellSort(){}

    public static void sort(Comparable[] arr){
        int n = arr.length;

        // 计算 increment sequence: 1, 4, 13, 40, 121, 364, 1093...
        int h = 1;
        while (h < n/3) 
            h = 3*h + 1;

        while (h >= 1) {
            // h-sort the array
            for (int i = h; i < n; i++) {

                // 对 arr[i], arr[i-h], arr[i-2*h], arr[i-3*h]... 使用插入排序
                Comparable e = arr[i];
                int j = i;
                for ( ; j >= h && e.compareTo(arr[j-h]) < 0 ; j -= h)
                    arr[j] = arr[j-h];
                arr[j] = e;
            }

            h /= 3;
        }
    }
}

    在实现中,使用了非常常用的让h递减的序列,在这种情况下,希尔排序的时间复杂度能达到O(n^3/2)这样的级别,相较而言,比O(n^2)级别的排序算法改变了非常多。希尔排序本身也是非常实用的方法,因为它的时间复杂度比O(n^2)低,虽然比O(nlogn)高一些,不过实现相对简单,所以在某些环境下,使用希尔排序会是一种首选。不管怎样,对于排序,最优时间复杂度是O(nlogn)级别的。

请使用手机"扫一扫"x

猜你喜欢

转载自blog.csdn.net/weixin_42491578/article/details/80769383
1.7