1. 程式人生 > >歸併排序,堆排序,快速排序的講解與實現

歸併排序,堆排序,快速排序的講解與實現

前言

本文主要講的是歸併排序,堆排序和快速排序的原理與實現。當然我的實現不一定不是最好的,如果有更好的實現大家也可以在評論區貼出更好的實現程式碼。

時間複雜度的計算:時間複雜度大概的意思是以每一條執行的語句為單位,一個排序演算法在隨著資料的增大時間上會以什麼形式去增長(這裡一個演算法可能根據資料情況的不同會有多個時間複雜度比如我排序的資料是1,2,3,4,5和1,3,2,5,4 使用快速排序的話就會產生不同的時間複雜度,一般我們取最常見的資料情況即可,即平均時間複雜度)。一般情況下我們關注時間複雜度關注到數量級即可比如某個演算法的執行程式碼量是n2 + logn那麼我們一般就記做O(n2)。

常見的時間複雜度有:

常數階O(1),對數階O(  ),線性階O(n),

線性對數階O(nlog2n),平方階O(n^2),立方階O(n^3)

歸併排序

歸併排序是一種穩定排序,時間複雜度為O(nlogn)。適用於:資料量不大,需要穩定排序的情況。

演算法原理:

歸併排序的思想,有點像MapReduce,是一種分治的思想,就是將一個大的陣列分成許多的小陣列,小陣列間進行排序之後再進行多個小陣列的聚合,聚合時根據小陣列內值得大小從新生成一個新的小陣列,直到聚合完畢。感覺這種演算法可以寫個多處理器的版本速度應該會更快。

演算法實現:

private static void mergeSort(int[] arr) {
        int step = 2;
        int[] temps = new int[arr.length];

        while (step < arr.length * 2) {
            for (int i = 0; i < arr.length; i += step) {
                int left = i, right = i + step / 2 < arr.length ? i + step / 2 : arr.length - 1;
                int index = i;

                while (index < i + step && index < arr.length) {
                    if (arr[left] > arr[right]) {
                        temps[index] = arr[right];
                        right++;
                        if (right == i + step || right == arr.length) {
                            index++;
                            break;
                        }
                    } else {
                        temps[index] = arr[left];
                        left++;
                        if (left == i + step / 2) {
                            index++;
                            break;
                        }
                    }

                    index++;
                }
                while (left < i + step / 2 && index < arr.length) {
                    temps[index] = arr[left];
                    left++;
                    index++;
                }
                while (right < i + step && index < arr.length) {
                    temps[index] = arr[right];
                    right++;
                    index++;
                }
            }
            step *= 2;

            int[] trans = arr;
            arr = temps;
            temps = trans;
        }
    }

堆排序

堆排序是一種穩定排序,平均時間複雜度為O(n * logn).適用於:資料量比較大(速度不一定快但是省空間),需要穩定排序的情況。其實堆排序的演算法更適用於查詢陣列內最大最小值。

演算法原理:

演算法實現:

 private static void heapSort(int[] arr) {
        int length = arr.length;
        while (length > 0) {
            int temp;
            for (int i = length / 2 - 1; i > -1; i--) {
                int k = i * 2;
                if (arr[i] < arr[k + 1]) {
                    temp = arr[i];
                    arr[i] = arr[k + 1];
                    arr[k + 1] = temp;
                }
                if (k + 2 < length && arr[i] < arr[k + 2]) {
                    temp = arr[i];
                    arr[i] = arr[k + 2];
                    arr[k + 2] = temp;
                }
            }
            temp = arr[--length];
            arr[length] = arr[0];
            arr[0] = temp;
        }
    }

快速排序

快速排序是一種不穩定排序,時間複雜度O(nlogn)。適用於:不需要穩定排序的前提下,大多數情況都是可以滿足的。快排同樣使用的是分治的思路,也是有多種快速排序演算法可以再多cpu或者多主機的情況下執行的。

演算法原理:

演算法實現:

private static void quickSort(int[] arr, int left, int right) {
        if(left >= right)
            return;

        int reference = arr[left], l = left, r = right;
        while(l != r) {
            while(arr[r] >= reference && l != r)
                r--;

            while(arr[l] <= reference && l != r)
                l++;

            if(l < r){
                int temp = arr[r];
                arr[r] = arr[l];
                arr[l] = temp;
            }
        }

        arr[left] = arr[l];
        arr[l] = reference;

        quickSort(arr, left, l - 1);
        quickSort(arr, l + 1, right);
    }

總結

每種演算法都有著各自最適合的情況,個人認為不要求穩定排序的情況下使用快速排序,要求穩定且資料量不是特別大的時候使用歸併排序,資料量的的時候堆排序感覺速度很慢也不是很合適的演算法。。感覺堆排序的優點還是能迅速的找出極大極小值吧。