從零開始學資料結構和演算法(一)冒泡與選擇排序

DevYK發表於2019-03-22

介紹

排序演算法可以分為內部排序和外部排序,內部排序是資料記錄在記憶體中進行排序,而外部排序是因排序的資料很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。常見的內部排序演算法有:插入排序、希爾排序、選擇排序、氣泡排序、歸併排序、快速排序、堆排序、基數排序等。用一張圖概括:

dsafadsf.webp

關於時間複雜度:

  1. 平方階 (O(n2)) 排序 各類簡單排序:直接插入、直接選擇和氣泡排序。
  2. 線性對數階 (O(nlog2n)) 排序 快速排序、堆排序和歸併排序。
  3. O(n1+§)) 排序,§ 是介於 0 和 1 之間的常數。 希爾排序。
  4. 線性階 (O(n)) 排序 基數排序,此外還有桶、箱排序。

關於穩定性:

穩定的排序演算法:氣泡排序、插入排序、歸併排序和基數排序。

不是穩定的排序演算法:選擇排序、快速排序、希爾排序、堆排序。

名詞解釋:

n:資料規模

k:“桶”的個數

In-place:佔用常數記憶體,不佔用額外記憶體

Out-place:佔用額外記憶體

穩定性:排序後 2 個相等鍵值的順序和排序之前它們的順序相同

氣泡排序演算法

氣泡排序(Bubble Sort)也是一種簡單直觀的排序演算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個演算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。

作為最簡單的排序演算法之一,氣泡排序給我的感覺就像 Abandon 在單詞書裡出現的感覺一樣,每次都在第一頁第一位,所以最熟悉。氣泡排序還有一種優化演算法,就是立一個 flag,當在一趟序列遍歷中元素沒有發生交換,則證明該序列已經有序。但這種改進對於提升效能來說並沒有什麼太大作用。

  • 動畫演示

    a.gif

  • 思想原理

    1. 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
    2. 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數。
    3. 針對所有的元素重複以上的步驟,除了最後一個。
    4. 持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
  • 程式碼實現

    public static void bubbleSort(int[] array){
        //3 1 5 8 2 9 4 6 7    n*(n-1)/2    n
        for(int i=array.length-1;i>0;i--) {
            boolean flag=true;
            for (int j = 0; j < i; j++) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                    flag=false;
                }
            }
            if(flag){
                break;
            }
        }
    }
    複製程式碼
  • 使用場景

    • 資料量足夠小,比如鬥牛遊戲的牌面排序

選擇排序演算法

  • 簡介

    選擇排序是一種簡單直觀的排序演算法,無論什麼資料進去都是 O(n²) 的時間複雜度。所以用到它的時候,資料規模越小越好。唯一的好處可能就是不佔用額外的記憶體空間了吧。

  • 動畫演示

    b.gif

  • 演算法步驟

    1. 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
    2. 再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。
    3. 重複第二步,直到所有元素均排序完畢。
  • 程式碼實現

    /**
    *選擇排序
    */
    public void selectSort(int[] arr) {
        for (int j = 0; j < arr.length - 1; j++) {
            //定義一個最小值
            int min = j;
            for (int i = j + 1; i < arr.length; i++) {
                if (arr[j] > arr[i]) {
                    min = arr[i];
                }
            }
            //如果第一位已經是最小值了就不用替換了 一定保證程式達到最優;
            if (min != j) {
                int temp = arr[min];
                arr[min] = arr[j];
                arr[j] = temp;
            }
        }
    }
    複製程式碼
  • 使用場景

    快速排序的基礎

參考例子

利用蠻力法給牌進行排序(氣泡排序)

  • 編寫 卡片 > 牌 資料 model

    /**
    * 牌的資料 Bean
    * 
    */
    public class Cards implements Comparable{
        public int pokerColors;//花色
        public int cardPoints;//點數
    
        public Cards(int pokerColors, int cardPoints) {
            this.pokerColors = pokerColors;
            this.cardPoints = cardPoints;
        }
        //提供一個方法,用來比較物件的大小
        @Override
        public int compareTo(@NonNull Object o) {
            Cards c=(Cards)o;
            if(this.cardPoints>c.cardPoints){
                return 1;
            }else if(this.cardPoints<c.cardPoints){
                return -1;
            }
            if(this.pokerColors>c.pokerColors){
                return 1;
            }else if(this.pokerColors<c.pokerColors){
                return -1;
            }
            return 0;
        }
    
        @Override
        public String toString() {
            return "Cards{" +
                    "pokerColors=" + pokerColors +
                    ", cardPoints=" + cardPoints +
                    '}';
        }
    
    }
    複製程式碼
  • 卡片進行排序

    public void testCards() {
        Cards [] cards = {new Cards(3,9),new Cards(1,10),new Cards(2,6)};
        for (int i = cards.length - 1; i > 0; i--) {
            for (int j = 0; j < i; j++) {
                if (cards[j].compareTo(cards[j+ 1] ) > 0 ){
                    Cards temp =cards[j];
                    cards[j] = cards[j+1];
                    cards[j+1] = temp;
                }
            }
        }
    }
    複製程式碼

資料結構系列文章

相關文章