java-歸併排序與快排的效率比較

java-歸併排序與快排的效率比較

用java完整地實現了歸併排序和快速排序,然後測試兩個算法在數組長度由100增加到一億過程中的時間複雜度,比較兩個算法效率。

歸併排序

public class MergeSort {

    public int[] A;

    public MergeSort(int[] array) {
        this.A = array.clone();
        sort(0, array.length - 1);
    }

    public void sort(int low, int high) {

        if (low < high) {
            int mid = (low + high) / 2;
            sort(low, mid);
            sort(mid + 1, high);
            merge(low, mid, high);
        }
    }

    public void merge(int low, int mid, int high) {

        // 聲明新的數組,臨時儲存歸併結果
        int[] B = new int[high - low + 1];
        int h = low;
        int i = 0;
        int j = mid + 1;

        while (h <= mid && j <= high) {
            if (A[h] <= A[j]) {
                B[i] = A[h];
                h++;
            } else {
                B[i] = A[j];
                j++;
            }
            i++;
        }

        // 等號很重要
        if (h <= mid) {
            for (int k = h; k <= mid; k++) {
                B[i] = A[k];
                i++;
            }
        } else {
            for (int k = j; k <= high; k++) {
                B[i] = A[k];
                i++;
            }
        }

        for (int k = low; k < high; k++) {
            A[k] = B[k - low];
        }

    }
}

快速排序

public class QuickSort {

    public int[] A;

    public QuickSort(int[] array) {
        this.A = array.clone();
        sort(0, array.length - 2);
    }

    public void sort(int p, int q) {
        if (p < q) {
            int j = partition(p, q);
            sort(p, j - 1);
            sort(j + 1, q);
        }
    }

    public int partition(int p, int q) {
        int j = q;
        int axis = A[p];
        int i = p;

        while (true) {

            //從左往右找到第一個比axis大的數字
            while (A[i] <= axis) {
                i++;
            };

            //從右往左找到第一個小於等於axis的數字
            while (A[j] > axis) {
                j--;
            };

            if (i < j) {
                swap(i, j);
            } else {
                break;
            }
        }

        A[p] = A[j];
        A[j] = axis;

        return j;
    }

    public void swap(int i, int j) {
        int tmp = A[i];
        A[i] = A[j];
        A[j] = tmp;
    }

}

效率比較

新建TimeCounter類,用來實例化兩個算法類,並測試兩個算法。size指定測試數組的長度,maximum 指定生成的數組中元素的最大值,注意:由於快排的數組最後一個數字要求最大,因此實際的數組長度是是size+1

import java.util.Arrays;
import java.util.Random;

public class TimeCounter {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        int size = 101;
        int maximum = 100000;
        int[] array = new int[size];

        for (int i = 0; i < array.length - 1; i++) {
            array[i] = new Random().nextInt(maximum);
        }

        //快排數組中的最後一個數字最大
        array[size - 1] = maximum + 1;

        long QuickStart = System.currentTimeMillis();
        new QuickSort(array);
        long QuickEnd = System.currentTimeMillis();

        long MergeStart = System.currentTimeMillis();
        MergeSort mergesort=new MergeSort(array);
        long MergeEnd = System.currentTimeMillis();

//       System.out.println(Arrays.toString(array));
//       System.out.println(Arrays.toString(mergesort.A));

        System.out.println("quick sort: " + (QuickEnd - QuickStart));
        System.out.println("merge sort: " + (MergeEnd - MergeStart));

    }
}

在同一臺計算機上,修改size的值,運行TimeCounter.java,得到兩個算法在不同數組長度下的執行時間:

數組長度 快速排序(運行時間/毫秒) 歸併排序(運行時間/毫秒)
100 0 0
1000 1 1
10000 1 3
100000 14 14
1000000 79 120
10000000 982 1186
100000000 55733 12328

結果

我們知道快排和歸併的理論上的時間複雜性如下表:

算法 最壞時間複雜性 平均時間複雜性
快速排序 n^2 n*log(n)
歸併排序 n*log(n) n*log(n)

1. 在數組長度小於一千萬的時候,如下圖,快速排序的速度要略微快于歸並排序,可能是因爲歸併需要額外的數組開銷(比如聲明臨時local數組用來儲存排序結果),這些操作讓歸併算法在小規模數據的並不佔優勢。

這裏寫圖片描述

2. 但是,當數據量達到億級時,歸併的速度開始超過快速排序了,如下圖,因爲歸併排序比快排要穩定,所以在數據量大的時候,快排容易達到O(n^2)的時間複雜度,當然這裏是指未改進的快排算法。

這裏寫圖片描述

個人博客:https://pengzhaoqing.github.io/posts/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章