常見排序算法性能測試

冒泡排序

  • 排序原理

冒泡排序方法是最簡單的排序方法。這種方法的基本思想是,將待排序的元素看作是豎着排列的“氣泡”,較小的元素比較輕,從而要往上浮。在冒泡排序算法中我們要對這個“氣泡”序列處理若干遍。所謂一遍處理,就是自底向上檢查一遍這個序列,並時刻注意兩個相鄰的元素的順序是否正確。如果發現兩個相鄰元素的順序不對,即“輕”的元素在下面,就交換它們的位置。顯然,處理一遍之後,“最輕”的元素就浮到了最高位置;處理二遍之後,“次輕”的元素就浮到了次高位置。在作第二遍處理時,由於最高位置上的元素已是“最輕”元素,所以不必檢查。一般地,第i遍處理時,不必檢查第i高位置以上的元素,因爲經過前面i-1遍的處理,它們已正確地排好序。

  • 複雜度的計算O(n^2)

對於n位的數列則有比較次數爲 (n-1) + (n-2) + … + 1 = n * (n - 1) / 2,這就得到了最大的比較次數
而O(N^2)表示的是複雜度的數量級。舉個例子來說,如果n = 10000,那麼 n(n-1)/2 = (n^2 - n) / 2 = (100000000 - 10000) / 2,相對10^8來說,10000小的可以忽略不計了,所以總計算次數約爲0.5 * N^2。用O(N^2)就表示了其數量級(忽略前面係數0.5)。

  • 代碼實現
/**
     * 冒泡排序算法
     */
    public static void bubble(int[] arr){
        for(int i = arr.length-1;i>=0;i--){//比較次數根據數組長度
            for(int j = 0;j<i;j++){
                if(arr[j]>arr[j+1]){//判斷前一個是否比後一個數大,是的話將位置掉換
                    change(j,j+1,arr);
                }
            }
        }
    }
/**
     * 將兩個變量值調換
     * @param a
     * @param b
     */
    private static void change(int a, int b,int[] arr) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

選擇排序

  • 排序思想

對比數組中前一個元素跟後一個元素的大小,如果後面的元素比前面的元素小則用一個變量k來記住他的位置,接着第二次比較,前面“後一個元素”現變成了“前一個元素”,繼續跟他的“後一個元素”進行比較如果後面的元素比他要小則用變量k記住它在數組中的位置(下標),等到循環結束的時候,我們應該找到了最小的那個數的下標了,然後進行判斷,如果這個元素的下標不是第一個元素的下標,就讓第一個元素跟他交換一下值,這樣就找到整個數組中最小的數了。然後找到數組中第二小的數,讓他跟數組中第二個元素交換一下值,以此類推。

  • 時間複雜度計算O(n^2)

總的比較次數N=(n-1)+(n-2)+…+1=n*(n-1)/2。交換次數O(n),最好情況是,已經有序,交換0次;最壞情況交換n-1次,逆序交換n/2次。交換次數比冒泡排序少多了,由於交換所需CPU時間比比較所需的CPU時間多,n值較小時,選擇排序比冒泡排序快。

  • 代碼實現
/**
     * 選擇排序
     */
    public static void select(int[] arr){
        for(int i = 0; i<arr.length -1;i++){//比較n-1次,每次遍歷得到最小數的索引
            int minIndex = i;   //默認第一個數的爲最小值的索引
            for(int j = i+1;j<arr.length;j++){  //每次從第i+1個數開始尋找最小數的索引
                if(arr[j]<arr[minIndex]){   
                    minIndex = j;
                }
            }
            change(i,minIndex,arr);//將該次遍歷得到的最小數與第i位數交換
        }
    }

插入排序

  • 排序原理

將初始序列中的第一個元素作爲一個有序序列,然後將剩下的 n-1 個元素按關鍵字大小依次插入該有序序列,每插入一個元素後依然保持該序列有序,經過 n-1 趟排序後使初始序列有序。

  • 時間複雜度計算 O(n^2)

如果目標是把n個元素的序列升序排列,那麼採用插入排序存在最好情況和最壞情況。最好情況就是,序列已經是升序排列了,在這種情況下,需要進行的比較操作需(n-1)次即可。最壞情況就是,序列是降序排列,那麼此時需要進行的比較共有n(n-1)/2次。插入排序的賦值操作是比較操作的次數加上 (n-1)次。平均來說插入排序算法的時間複雜度爲O(n^2)。

  • 代碼實現
/**
     *插入排序
     */
    public static void insert(int[] arr){
        for(int i=1;i<arr.length;i++){//定義外層循環,從第二個數開始進行比較
            int temp = arr[i];//記錄每次循環需要比較的數
            int j = i-1;//設置要比較的短數組的長度(初始序列長度)
            for(;j>=0;j--){
                if(arr[j]>temp){//如果前面的數比temp大,則將該書後移到j+1的位置
                    arr[j+1] = arr[j];
                }else{//如果前面的數比temp小,終止循環,將該數的下個位置的值改爲temp
                    arr[j+1] = temp;
                    break;
                }
            }//循環結束後j==-1
            if(j==-1){//當前面的數都比temp小時,將第一個數的值改爲temp
                arr[0] = temp;
            }
        }
    }

快速排序

  • 排序原理

找一個值作爲參考值,比參考值大的就放在右邊,比參考值小的就放在左邊。那麼一趟完成後就將數組分成了兩部分:參考值左邊的都是小於參考值的數,參考值右邊的都是大於參考值的數,然後分別遞歸求這兩部分,最後得到的就是一個排好序的數組了。

  • 時間複雜度計算O(n)

快速排序每次將待排序數組分爲兩個部分,在理想狀況下,每一次都將待排序數組劃分成等長兩個部分,則需要logn次劃分。
而在最壞情況下,即數組已經有序或大致有序的情況下,每次劃分只能減少一個元素(中間位置元素),這樣的結果就好比是冒泡排序,所以快速排序時間複雜度下界爲O(nlogn),最壞情況爲O(n^2)。
- 代碼實現

public static void quicksort(int[] arr,int low, int high){
        int l = low;
        int h = high;
        int key = arr[low];//每次比較將左側的第一個數作爲關鍵數

        while(l<h){
            while(l<h && arr[h]>=key) h--;//左右沒有出現交叉,並且右邊沒有找到比關鍵數小的,則索引左移
            if(l<h){
                //從右邊找到了將找到的比關鍵數小的值與左邊索引對應的值交換,再從左側開始找,左側索引值右移移
                int temp = arr[l];
                arr[l] = arr[h];
                arr[h] = temp;
                l++;
            }
            while(l<h && arr[l]<=key) l++;//左右兩邊沒有出現交叉,並且左邊沒由找到比關鍵數大的,則索引右移
            if(l<h){
                //將在左側找到的比關鍵數大的值與右邊索引對應的值交換,開始從右側查找,右側索引值左移
                int temp = arr[h];
                arr[h] = arr[l];
                arr[l] = temp;
                h--;
            }
        }//當l==h出現交叉時,跳出循環,也就是找到了中間點,接下來從中間點左右分開,再次分別查找
        //此時左邊的數均小於右邊的數
        if(l>low){//l>low代表沒由找到比關鍵數大的數,那麼就需要跟換關鍵數從新找
            quicksort(arr,low,l-1);
        }
        if(h<high){
            quicksort(arr,l+1,high);
        }
}

在這裏再介紹下隨機化快排:
隨機化快排是建立在基本快排的算法上做出的改進,利用概率事件降低快排出現的最不利情況的可能性,也就是說:基本的快速排序選取第一個元素作爲關鍵數。這樣在數組已經有序的情況下,每次劃分將得到最壞的結果。而隨機快排是選取一個元素作爲關鍵數。這種情況下雖然最壞情況仍然是O(n^2),但最壞情況不再依賴於輸入數據,而是由於隨機函數取值不佳。實際上,隨機化快速排序得到理論最壞情況的可能性僅爲1/(2^n)。所以隨機化快速排序可以對於絕大多數輸入數據達到O(nlogn)的期望時間複雜度。

測試

這裏使用十萬條數據進行測試,分別用每一種算法進行(之所以使用十萬條數據而不是百萬條千萬條,是受到個人電腦配置限制,而且快排時使用到遞歸後,數據量過大,造成棧內存溢出)

這裏寫圖片描述

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