1. 程式人生 > >選擇排序,插入排序,氣泡排序超簡單教程(能有效的區分)。Java語言描述

選擇排序,插入排序,氣泡排序超簡單教程(能有效的區分)。Java語言描述

首先,我相信一個東西最重要的是思想,所以,本文重點關注這三個排序背後的思想。希望能幫你理解並分清這三個排序。

注:n代表一組數的個數.

i 代表任意的某個數( 0 < i < n + 1).

使用從小到大,從前到後的排序。(實際中,順序完全可以相反,此時為了教學方便)

前修知識:時間複雜度。 陣列下標從0開始。如何交換兩個數。

目錄

2 插入排序

拓展部分

3 氣泡排序

拓展部分

1.選擇排序

1.1 用具體例子說明:

現在有十名同學要按身高排隊。需要你的幫助。

  1. 於是你看了一下十人,找到了其中最低的人,讓他站在了第一位。
  2. 找到剩下九人中個子最低的人,讓她站在了第二位。
  3. 找到剩下八人中個子最低的人,讓它站在第三位。
  4. ............
  5. 找到剩下一人中個子最低的人,讓TA站在第十位。//注:我們一般省略此步。因為如果前九位是最小的九位,那麼最後一位一定是最大值。
  6. 此時完成排序,十人身高有序。

1.2 排序思想

選擇排序是最符合機器思維的一個排序,因為人們機器最喜歡做的事情就是暴力了,選擇排序就是一個簡單的暴力加上減而治之的思想。

1.3 見名知意

選擇排序:使用選擇進行排序,每次選出最小值

1.4 抽象過程:

  1. 對於任意一個待排序陣列,從第 1 位遍歷一次陣列,找到最小值,使其與第 1 位交換。
  2. 從第二位開始遍歷一次陣列,找到其中的最小值,使其與第二位進行交換
  3. 一般化:從第 i 位遍歷一次陣列,找到第 i 小的值,使其與第 i 位交換。
  4. 最後找到最大值,此時一定在最後一位。不需要進行交換。

1.5 例項操作

一般n位的陣列,我們只需要遍歷 n-1次即可,因為最後一位一定是最大值。

1.6 程式碼實現(JAVA版本):

    /**
     * 
     * Description: 選擇排序JAVA語言描述
     * 
     * @Title: selectSort
     * @param a
     *            傳入陣列(排序以陣列為物件,方便。) 
     * void
     */
    public static void selectSort(int[] a) { // 選擇排序.
        int len = a.length; // 得到陣列長度
        for (int i = 0; i < len; i++) {// 從第 0 位置開始找
            int min = i;
            for (int j = i + 1; j < len; j++) { // 找最小值對應的位置
                if (a[j] > a[min])
                    min = j; // 只要比min小,就改變min的值,確保min最後儲存的值為最小值的位置
            }
            exch(a, i, min); // 將找到的最小值放到合適的位置(即第 i 位置)
        }
    }

1.7 程式碼實現(C語言版)

TODO

1.8 演算法分析

  1. 這個演算法的複雜度約為 : N² / 2 次比較,N次交換。
  2. 空間複雜度:只借用了常數級別的空間,可以忽略。

1.9 拓展知識

  • 減而治之:在選排中,我們的第 i 次迴圈,總是從第 i 位開始,那麼遍歷個數為:n, n-1, n-2..........3,2.隨著迴圈次數的增加,待遍歷的個數減少,就是減而治之。也就是說:隨著程式碼的執行,篩選範圍逐個減少就是減而治之。與之相關的還有分而治之。
  • 選擇排序在所有情形下都是O(n²)的。
  • 選擇排序是所有排序中最簡單,但也是思想最通用的排序,其他排序很多都有這種找出最小值後減而治之的思想。

2 插入排序

2.1 用具體例子說明

依然是十人身高排隊問題。

  1. 讓第一個人另起一排。
  2. 讓第二個人站到另起一排的合適位置。
  3. 讓第三個人站到另起一排的合適位置。
  4. .......
  5. 當最後一人站到另起一排的合適位置時,排序結束, 另起的一排就是十人按身高的順序。

2.2 排序思想

插入排序:認為第一位單獨為一個有序陣列,將其他值挨個插入到這個有序陣列中,並且始終保持有序。當其餘 n - 1位插入完成,排序結束。

2.3 見名知意

插入排序經典之處在於把陣列排序問題轉化成了新數值插入到已有的有序陣列的問題。所以稱之為插入排序。

2. 4 抽象過程

  1. 認為第一位數為一個已經排序好的陣列haveSort。// 只有一個數的有序陣列。
  2. 將第二位數插入到我們邏輯中的陣列 haveSort 中去,並保持邏輯陣列haveSort有序。
  3. 將第三位數插入到我們邏輯中的陣列 haveSort 中去,並保持邏輯陣列haveSort有序。
  4. ..........
  5. 將最後一位數插入到我們邏輯中的陣列 haveSort 中去,並保持邏輯陣列haveSort有序。
  6. 此時原陣列與我們的邏輯陣列haveSort重合,且haveSort有序,所以原陣列排序結束。

注:haveSort陣列不存在,只是我們在邏輯上劃分出來的陣列,它本質上就是原陣列的前 i 位陣列成的數,不過有序而已。

2.5 例項操作

2.6 程式碼實現(JAVA版本)

    /**
     * 
     * Description: 插入排序Java程式碼版
     * 
     * @Title: insertSort
     * @param a 待排序陣列。
     *            void
     */
    public void insertSort(int[] a) { // 插入排序
        int len = a.length; // 獲得陣列的長度。即外迴圈次數。
        for (int i = 1; i < len; i++) {
            for (int j = i; j > 0 && a[j] < a[j - 1]; j--) {
                // 從尾部開始交換位置,直到到達合適位置。 此時插入完成。
                exch(a, j, j - 1);  // 交換相鄰的數字,實現插入。
            }
        }
    }

2.7 程式碼實現(C語言版)

TODO

2.8 演算法分析

時間 :(N)~N²/2 次比較和0 ~N²/2 次交換

拓展部分

  • 插入排序適用於基本有序的情況,當數字基本有序時,或者陣列中的每個元素距離它的最終位置都不遠,排序時間就接近於線性排序.
  • 一個有序的大數組合並一個小陣列時,或者兩個有序陣列的合併時,使用插入排序的思想也很快可以解決。 //較常用
  • 插入排序最壞情況是O(N²)的,但最好情形是O(n)的,陣列越有序,插入排序越快.
  • 一般對鏈式結點鏈是比較困難的一件事,我們可以使用插入排序對鏈式結點鏈進行排序.
  • 希爾排序就是插入排序的改進版,最壞情況降低到O(N^1.5),顯著提高.但是破壞了插入排序的穩定性。

3 氣泡排序

3.1 用具體例子說明

十個人排隊。需要你的幫助,個鬼啦,不要你的幫助。

氣泡排序才是我們真正生活中使用的排隊。

每個人都告訴後一個人:你如果比我低,請和我交換位置。

假設大家有序從後向前詢問,每3秒開始一次詢問,用最多30秒,就完成了排序。

  • 第一次詢問
    • 第1個人向第2個人發出請求,如果第1人比第2人高,那麼交換。
    • 第2個人向第3個人發出請求,如果第2人比第3人高,那麼交換。
    • 第3個人向第4個人發出請求,如果第3人比第4人高,那麼交換。
    • .........
    • 第9個人向第10個人發出請求,如果第9人比第10人高,那麼交換。
    • 第一次詢問結束。此時第十個人一定是最高的身高,因為最高身高的人每次請求都進行了交換,所以到了隊尾。
  • 第二次詢問
    • 第1個人向第2個人發出請求,如果第1人比第2人高,那麼交換。
    • 第2個人向第3個人發出請求,如果第2人比第3人高,那麼交換。
    • 第3個人向第4個人發出請求,如果第3人比第4人高,那麼交換。
    • .........
    • 第8個人向第9個人發出請求,如果第8人比第9人高,那麼交換。
    • 第二次詢問結束。此時第九個人一定是第二高的身高,因為第二高身高的人每次請求都進行了交換,所以到了隊尾。
  • 第三次詢問
  • 第四次詢問
  • ........
  • 第九次詢問
  • 第一人一定是最低,所以不進行詢問,10-1 = 9 次詢問,完成排序。

注: 說白了就是模擬真實情況下的排隊,發揮每個人的力量去排序,但是計算機畢竟是單執行緒,所以儘可能的模擬就造成了需要多次遍歷詢問的結果

3.2 排序思想

明顯的看到了分而治之的影子,而且氣泡排序也是某種意義上的選擇排序,每次遍歷推選出最大的值,但是不同之後在於,冒泡不是直接交換最大值和最後一位,而是每次都像氣泡一樣,諸位詢問,諸位交換。

3.3 見名知意

這個排序就像一堆氣泡,想象一下:一堆氣泡,起點不同,大家同時向上奔跑,所有值,都在 "嘟嘟嘟" 的向上冒著。而跑得慢的氣泡就被跑得快的氣泡趕超過去。最終從上向下數去:越大的氣泡位置越高,實現有序。

3.4 抽象過程

與3.1具體例子說明相同,此處不再贅述

3.5 例項操作

3.6 程式碼實現(JAVA版本)

    /**
     * 
     * Description: 冒泡
     * 
     * @Title: bubbleSort
     * @param a
     *            待排序陣列 void
     */
    public void bubbleSort(int[] a) {
        int len = a.length; // 得到 a[] 的容量
        for (int i = 0; i < a.length; i++) { // 詢問次數
            for (int j = i; j < a.length; j++) { // 進行冒泡
                if (a[i] > a[j]) {
                    exch(a, j, i); // 不關心交換的細節、
                }
            }
        }
    }

3.7 程式碼實現(C語言版)

TODO

拓展部分

著名的堆排序就是建立在氣泡排序和二叉樹上發展而來的。

其中,SEL是(select )選擇排序,INS是 (insert)插入排序

本部落格的所有原始碼

package sort;

import java.util.Iterator;

public class BaseSort {
    /**
     * 
     * Description: 選擇排序JAVA語言描述
     * 
     * @Title: selectSort
     * @param a
     *            傳入陣列(排序以陣列為物件,方便。) void
     */
    public void selectSort(int[] a) { // 選擇排序.
        int len = a.length; // 得到陣列長度
        for (int i = 0; i < len; i++) {// 從第 0 位置開始找
            int min = i;
            for (int j = i + 1; j < len; j++) { // 找最小值對應的位置
                if (a[j] > a[min])
                    min = j; // 只要比min小,就改變min的值,確保min最後儲存的值為最小值的位置
            }
            exch(a, i, min); // 將找到的最小值放到合適的位置(即第 i 位置)
        }
    }

    /**
     * 
     * Description: 插入排序Java程式碼版
     * 
     * @Title: insertSort
     * @param a
     *            待排序陣列。 void
     */
    public void insertSort(int[] a) { // 插入排序
        int len = a.length; // 獲得陣列的長度。即外迴圈次數。
        for (int i = 1; i < len; i++) {
            for (int j = i; j > 0 && a[j] < a[j - 1]; j--) {
                // 從尾部開始交換位置,直到到達合適位置。 此時插入完成。
                exch(a, j, j - 1); // 不關心交換細節
            }
        }
    }

    /**
     * 
     * Description: 冒泡
     * 
     * @Title: bubbleSort
     * @param a
     *            待排序陣列 void
     */
    public void bubbleSort(int[] a) {
        int len = a.length; // 得到 a[] 的容量
        for (int i = 0; i < a.length; i++) { // 詢問次數
            for (int j = i; j < a.length; j++) { // 進行冒泡
                if (a[i] > a[j]) {
                    exch(a, j, i); // 不關心交換的細節、
                }
            }
        }
    }

    /**
     * Description: 傳入一個數組,交換其中下標為j和i的值
     * 
     * @Title: exch
     * @param a
     * @param j
     * @param i
     *            void
     */
    private void exch(int[] a, int j, int i) {
        int t = a[j];
        a[j] = a[i];
        a[i] = t;
    }

}