演算法(三)初等排序後篇[選擇和氣泡排序]

劉望舒發表於2017-04-06

相關文章
演算法(一)時間複雜度
演算法(二)初等排序前篇[插入和希爾排序]

1.選擇排序

根據上一篇文章講到的插入排序和希爾排序,我們把選擇排序的陣列也分為已排序部分和未排序部分。

圖解選擇排序

在用圖來講解選擇排序之前,我們要先了解選擇排序的規則。
選擇排序的規則
就是重複執行以下的處理:
1.找出未排序部分最小值的位置min。
2.將min位置的元素與未排序部分的起始元素做對比,如果順序錯誤則將它們進行就交換。

以陣列a={5,4,8,7,9,3,1}為例,我們對其進行從小到大排序,排序過程如下圖所示。

演算法(三)初等排序後篇[選擇和氣泡排序]

首先我們找到未排序部分最小的值a[6]=1和未排序的起始元素a[0]=5相比較,發現a[6]的值更小,則將兩個數進行交換,以此類推。
演算法(三)初等排序後篇[選擇和氣泡排序]

最終得到的結果為a={1,3,4,5,7,8,9}

實現選擇排序

接下來要實現插入排序,針對下圖來定義變數。

演算法(三)初等排序後篇[選擇和氣泡排序]

如上圖所示,i為迴圈變數,代表未排序部分的開頭元素; j為迴圈變數,用來遍歷未排序部分找出最小值min。
接下來我們用程式碼來實現選擇排序,如下所示。

public class SelectionSort {
    public static void main(String[] args) {
        int a[] = {5, 4, 8, 7, 9, 3, 1};
        ArrayUtils.printArray(a);
        int b[] = selection(a);
        ArrayUtils.printArray(b);
    }

    public static int[] selection(int[] a) {
        int i, j, min, v;
        int n = a.length;
        for (i = 0; i < n; i++) {
        //每次將未排序部分的首元素下標賦值給下標min
            min = i;
            //得到未排序部分的最小值的下標並賦值給min
            for (j = i+1; j < n; j++) {
                if (a[j] < a[min]) {
                    min = j;
                }
            }
            v = a[i];
            a[i] = a[min];
            a[min] = v;
        }
        return a;
    }
}複製程式碼

其中負責列印陣列的ArrayUtils類如下所示。

public class ArrayUtils {
    public static void printArray(int[] array) {
        System.out.print("{");
        int len=array.length;
        for (int i = 0; i < len; i++) {
            System.out.print(array[i]);
            if (i < len - 1) {
                System.out.print(", ");
            }
        }
        System.out.println("}");
    }
}複製程式碼

輸出結果為:
{5, 4, 8, 7, 9, 3, 1}
{1, 3, 4, 5, 7, 8, 9}

選擇排序複雜度

假設資料總數為n,則為了搜尋未排序部分最小的的值需要(n-1)+(n-2)+(n-3)+……+1次比較,也就是n²/2+n/2次比較,因此時間複雜度為O(n²)。同樣的,此前講過的插入排序和氣泡排序的時間複雜度也是O(n²)。它們的區別就是:不含flag的冒泡演算法和選擇排序並不依賴於比較運算的次數,不受輸入資料的影響,而插入演算法卻依賴於比較運算的次數,處理某些資料時會具有很高的效率。

2.氣泡排序

氣泡排序應該是開發者最容易理解的排序演算法,它的基本思想就是每次比較兩個相鄰的元素,如果它們的順序錯誤就把它們交換過來。需要進行排序的元素則向水中的氣泡一樣慢慢的移向水面。

圖解氣泡排序

與選擇排序一樣,需要進行氣泡排序的陣列也分為已排序部分和未排序部分。
氣泡排序的規則為:從陣列末尾開始依次比較相鄰的兩個元素,如果大小關係相反則交換位置,直到陣列中不再有順序相反的相鄰元素。

我們對陣列 a={5,3,2,4,1} 進行從小到大排序,排序過程如下所示。
第一輪排序:

演算法(三)初等排序後篇[選擇和氣泡排序]

我們將陣列末尾的a[4]的值和a[3]的值進行對比,發現a[4]的值比a[3]的值小,則將它們交換,再接著對剩下的相鄰的兩個元素進行對比和交換,最終得到的結果為a={1,5,3,2,4},已排序的部分的元素為1。

第二輪排序:

演算法(三)初等排序後篇[選擇和氣泡排序]

首先對比a[3]和a[4]的值,發現a[3]的值比a[4]的值小,則不需要進行排序。最終得到的結果為a={1,2,5,3,4},已排序部分的元素為1、2。

第三輪排序:

演算法(三)初等排序後篇[選擇和氣泡排序]

經過第三輪排序,已排序部分的元素為1、2、3。

第四輪排序:

演算法(三)初等排序後篇[選擇和氣泡排序]

經過四輪排序我們最終得到的結果為a={1,2,3,4,5}

實現氣泡排序

實現插入排序時,我們要先定義兩個變數,i為迴圈變數,表示未排序部分的開頭元素,從陣列開頭向末尾移動。j也為迴圈變數,用於對未排序部分中相鄰元素兩兩比較,從陣列的末尾n-1開始減小到 i 結束(i=1)。

演算法(三)初等排序後篇[選擇和氣泡排序]

程式碼實現如下所示。

public class BubbleSort {
    public static void main(String[] args) {
        int a[] = {5, 3, 2, 4, 1};
        ArrayUtils.printArray(a);
        int b[] = bubble(a);
        ArrayUtils.printArray(b);
    }

    public static int[] bubble(int[] a) {
        int i, j, v;
        int n = a.length;
        for (i = 1; i <= n - 1; i++) {
            for (j = n - 1; j >= i ; j--) {
                if (a[j] < a[j - 1]) {
                    v = a[j];
                    a[j] = a[j - 1];
                    a[j - 1] = v;
                }
            }
        }
        return a;
    }
}複製程式碼

其中ArrayUtils的printArray方法此前講過,這裡就不再給出,列印結果為:
{5, 3, 2, 4, 1}
{1, 2, 3, 4, 5}

氣泡排序的複雜度

最壞的情況下,氣泡排序對未排序部分的相鄰元素進行了(n-1)+(n-2)+(n-3)+……+1次比較,也就是n²/2-n/2次比較,根據推導大O階的規則我們得出氣泡排序的時間複雜度為O(n²)。

github原始碼


歡迎關注我的微信公眾號,第一時間獲得部落格更新提醒,以及更多成體系的Android相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,即可關注。

演算法(三)初等排序後篇[選擇和氣泡排序]

相關文章