1. 程式人生 > >常見的演算法:二分法查詢,氣泡排序和選擇排序

常見的演算法:二分法查詢,氣泡排序和選擇排序

今天主要說一下常見的演算法 ,於是我百度了一下點進那 “ 牛逼 ” 的知乎看了一眼 ,完蛋了 ,這都是些神馬 ??? 我怎麼一個都不會呢 ,我要的可是那種很常見的演算法啊 ,好吧 ,無形中又被深深的傷了一刀 ,好在我迅速調節狀態 。管他呢 ,我就按照自己 low 的來吧 。進入正題 ,我要說的幾種演算法就是二分法查詢 ,氣泡排序和選擇排序 。以陣列為例 ,談談它們在 Java 中的實現 。

二分法查詢 ,舉個例子說明一下 ,我們在玩猜數字遊戲的時候經常會用到 。小明給出數的範圍 ,0 ~ 100 ,我們猜 50 ,小明回答說 “ 大了 ” ,那我們會接著猜 25 ,就這樣我們一次次的逼近了真相 。這就是二分法的思想 。也可以說是分治的思想 ,分而治之 ,逐一攻破 。我們同樣也可以將這種思想應用到有序陣列的查詢上 。我們可以拿陣列中間的數和待查詢元素進行比較 ,其過程類似與猜數字 ,變得就是 “ 中間 ” 位的那個數 。最終我們可以找到目標元素 。方法實現 :

private static int binarySearch(int[] list,int target) {
        int low = 0;
        int high = list.length - 1;
        //直到low>high時還沒找到關鍵字就結束查詢,返回-1
        while(low<=high){
        int mid = (low+high) / 2;
        if(target < list[mid]){
            high = mid - 1;
        }
        else
if(target > list[mid]){ low = mid + 1; } else if(target == list[mid]){ return mid; } } return -1; }

氣泡排序 ,想象一下魚兒在水中吐了一個泡 ,在向上的過程中 ,是不是在越變越大 。其實氣泡排序的思想也就是這樣的 。給你 n 個數 ,我們用第一個數和第二個數進行比較 ,大的那個就放第二位 ,然後第二位和第三位比較 ,大的那個放在第三位 ,依次比下去 ,待到和最後一個比較結束 ,我們就能得到這 n 個數中最大的那個 ,且放在最後一個位置上 。( 這要是不明白就回頭再讀一遍 ) 這樣一輪比較結束我們得到一個最大的數 ,第二輪我們再進行比較 ,同理 ,可以得到剩餘 n - 1 個數中最大的數 。且放在倒數第二位 ,這就就是內層 for 迴圈條件 - 1 的原因 ,因為沒必要和上一輪的最後一個數進行比較嘛 。方法實現 :

public static void bubbleSort(int[] arr) {
     /*
     * 外面的for迴圈決定一個長度為 n 的資料要比較多少輪才能完成排序。
     * 裡面的for迴圈決定每次一輪迴圈中要做多少次才能結束。
     */
     for(int i = 0; i < arr.length - 1; i++) {
         for(int j = 0; j < arr.length - 1 - i; j++){
             //從小到大,大的值放後面位置。 
             if (arr[j] > arr[j+1]){
                int temp = arr[j]
                arr[j] = arr[j + 1]
                arr [j + 1] = temp 
             }
         }
     } 
}

PS . 只需要換一個符號 ,就可以得到從大到小的序列 。

快速排序 ,快速排序是對氣泡排序的一種改進 。它的基本思想是將要排序的資料分割成獨立的兩部分 ,其中一部分的所有資料都比另外一部分的所有資料都要小 。然後在按照此方法對兩部分資料分別進行快速排序 ,最終得到一個有序的序列 。需要說明的是 ,快速排序的平均時間複雜度是 O(nlogn) 。但是時間複雜度還是 O(n^2) ,因為時間複雜度是按照最壞結果來計算的 。為了方便理解 ,使用一組資料模擬一下排序的過程 。

假設要排的陣列為:int[] a = { 5 2 8 9 2 3 4 9 };

選擇 key = 5, 開始時 i = 0j = 7

下標         0    1    2    3    4    5    6    7

開始         5    2    8    9    2    3    4    9
            i                                  j  

第一次找     5    2    8    9    2    3    4    9
                      i                   j

交換:       5    2    4    9    2    3    8    9 
                      i                   j

第二次找     5    2    4    9    2    3    8    9
                           i         j

交換:       5    2    4    3    2    9    8    9
                           i         j

第三次找     5    2    4    3    2    9    8    9
                               ij

調整key:    2    5    4    3    5    9    8    9
                               ij

方法實現 :

private static void quickSort(int[] a, int low, int high) {  
        //找到遞迴演算法的出口  
        if( low > high) {
            return;
        }
        int i = low;  
        int j = high;  
        //預設 key
        int key = a[low];  
        //開始一趟排序
        while( i < j) {
            //先從右往左找到第一個小於 key 的數 ,
            //這麼做是因為在與 key 值交換的時候,保證了交換的數小於現有的 key 值
            //若是大於的話,j 指標就會繼續向左移動 。
            while(i<j && a[j] > key){  
                j--;  
            }
            //從左往右找到第一個大於等於 key 的數  
            while( i<j && a[i] <= key) {  
                i++;  
            }
            //交換,達到以 key “分治” 的效果
            if(i<j) {
                int temp = a[i];  
                a[i] = a[j];  
                a[j] = temp;  
            }  
        }
        // 當 i = j 時,調整 key 的值為 a[i] == a[j]  
        int temp = a[i];  
        a[i] = a[low];  
        a[low] = temp;  
        //對 key 左邊的數快速排序
        quickSort(a, low, i-1 );  
        //對 key 右邊的數快速排序  
        quickSort(a, i+1, high);  
}

後記 :不得不說 ,這些簡單的演算法我都沒有很熟悉 ,只能是那種原理清楚 ,但是實現起來卻是磕磕絆絆的狀態 。還有 ,以上給出的實現是可以進一步優化的 ,記得之前在面試的時候被問到過氣泡排序的問題 ,現場也被問到過如何優化 。