資料結構與算法系列十一(氣泡排序)
1.引子
1.1.為什麼要學習資料結構與演算法?
有人說,資料結構與演算法,計算機網路,與作業系統都一樣,脫離日常開發,除了面試這輩子可能都用不到呀!
有人說,我是做業務開發的,只要熟練API,熟練框架,熟練各種中介軟體,寫的程式碼不也能“飛”起來嗎?
於是問題來了:為什麼還要學習資料結構與演算法呢?
#理由一: 面試的時候,千萬不要被資料結構與演算法拖了後腿 #理由二: 你真的願意做一輩子CRUD Boy嗎 #理由三: 不想寫出開源框架,中介軟體的工程師,不是好廚子
1.2.如何系統化學習資料結構與演算法?
我想好了,還是需要學習資料結構與演算法。但是我有兩個困惑:
1.如何著手學習呢?
2.有哪些內容要學習呢?
學習方法推薦:
#學習方法 1.從基礎開始,系統化學習 2.多動手,每一種資料結構與演算法,都自己用程式碼實現出來 3.思路更重要:理解實現思想,不要背程式碼 4.與日常開發結合,對應應用場景
學習內容推薦:
資料結構與演算法內容比較多,我們本著實用原則,學習經典的、常用的資料結構、與常用演算法
#學習內容: 1.資料結構的定義 2.演算法的定義 3.複雜度分析 4.常用資料結構 陣列、連結串列、棧、佇列 散列表、二叉樹、堆 跳錶、圖 5.常用演算法 遞迴、排序、二分查詢 搜尋、雜湊、貪心、分治 動態規劃、字串匹配
2.考考你
在上一篇:資料結構與算法系列十(排序概述)中,我們列舉了常用的排序演算法,以及分析瞭如何綜合衡量排序演算法的優劣。如果你還沒有看上一篇的內容,可以去看一看,應該會有所收穫。
從這一篇開始,我們把每一種排序演算法,從演算法的思想,到程式碼實現都做一個分享。那麼你準備好了嗎?
我們這一篇的主角是:氣泡排序
#考考你: 1.你知道氣泡排序的核心思想嗎? 2.你能用java實現氣泡排序嗎? 3.你能寫出更優秀的氣泡排序程式碼嗎?
3.案例
3.1.氣泡排序思想
假設有一個待排序序列:[4, 5, 6, 3, 2, 1]。我們需要按照升序進行排序,排序後的序列是這樣的:[1, 2, 3, 4, 5, 6]。
如何通過氣泡排序實現呢?
這裡我們先來理解氣泡排序中的冒泡兩個字。所謂冒泡就像平靜的水面,魚兒從水底吹氣一樣,一個一個的水泡向上冒,很詩情畫意,我們都向往這樣的生活環境對吧。
那麼請保持這個美好的姿勢,我們一起來理解氣泡排序的思想,先看一個圖:
氣泡排序核心思想:
假設待排序序列有n個元素,需要經過n次冒泡,每一次冒泡過程中依次比較交換相鄰的兩個元素,一次冒泡結束,都會有1個元素到達指定的目標位置。這裡的關鍵詞有:
1.n個元素,n次冒泡 2.比較交換相鄰元素
3.2.氣泡排序程式碼實現
3.2.1.排序程式碼
/** * 氣泡排序:普通實現版本 * @param array:待排序陣列 * @param n:待排序陣列大小 */ public static void sort_1(Integer [] array,int n){ // 如果排序陣列規模小於等於1,直接返回 if(n <= 1){ return; } // 有n個元素,進行n次冒泡 for(int i = 0; i < n; i++){ // 每一次冒泡,比較交換相鄰兩個元素 for(int j = 0; j < n-i-1; j++){ if(array[j] > array[j+1]){ int tmp = array[j]; array[j] = array[j+1]; array[j+1] = tmp; } } } }
3.2.2.測試程式碼
public static void main(String[] args) { // 初始化測試陣列 Integer[] array = {4,5,6,3,2,1}; // 排序前 System.out.println("1.排序前陣列:" + Arrays.deepToString(array)); // 排序後 sort_1(array,array.length); // 排序後 System.out.println("2.排序後陣列:" + Arrays.deepToString(array)); }
測試結果:
D:\02teach\01soft\jdk8\bin\java com.anan.algorithm.sort.BubbleSort 1.排序前陣列:[4, 5, 6, 3, 2, 1] 2.排序後陣列:[1, 2, 3, 4, 5, 6] Process finished with exit code 0
3.3.氣泡排序實現優化
3.3.1.優化分析
在3.2.1節氣泡排序普通實現版本,我們嚴格按照氣泡排序的思想:n個元素、n次冒泡,每一次冒泡依次比較交換相鄰元素。實現了一個氣泡排序。
在這裡,請你先簡單思考一下:有沒有更優化的實現方式呢?
我們先來分析一下氣泡排序演算法的時間複雜度,結合程式碼我們發現氣泡排序的時間複雜度是:O(n^2),有兩次for迴圈,這不是一個高效的演算法對吧。如果說我們能夠減少冒泡的次數,則可以極大提升演算法的執行效率。
問題來了:什麼情況下可以減少冒泡次數呢?
其實我們只要結合氣泡排序演算法的核心思想後半部分:比較交換相鄰的元素。如果說在一次冒泡中,沒有發生相鄰元素的交換,那說明待排序序列已經有序了,不管後面還剩下多少次冒泡,我們都不需要再進行冒泡下去了。這樣是不是就減少冒泡的次數了呢
關於減少冒泡次數的分析,如果你暫時沒有理解過來的話,沒有關係。請看我們下面的程式碼實現,相信結合程式碼你會恍然大悟。
3.3.2.優化程式碼實現
/** * 氣泡排序:優化實現版本 * @param array:待排序陣列 * @param n:待排序陣列大小 */ public static void sort_2(Integer [] array,int n){ // 如果排序陣列規模小於等於1,直接返回 if(n <= 1){ return; } // 優化標識 // 如果某一次冒泡過程中,沒有發生資料交換 // 則說明已經排好了序,不需要在繼續冒泡 boolean flag = false; // n個元素,n次冒泡 for(int i = 0; i < n; i++){ // 重置是否發生交換標識 flag = false; // 每一次冒泡中,比較交換相鄰元素 for(int j = 0; j < n-i-1; j++){ if(array[j] > array[j+1]){ int tmp = array[j]; array[j] = array[j+1]; array[j+1] = tmp; // 發生了資料交換 flag = true; } } // 一次冒泡結束,檢查是否發生了資料交換 // 如果沒有發生資料交換,說明序列已經有序,不需要再繼續冒泡了 System.out.println("第【" + (i+1) + "】次冒泡."); if( !flag){ break; } } }
3.3.3.測試程式碼
public static void main(String[] args) { // 初始化測試陣列 Integer[] array = {4,5,6,3,2,1}; // 排序前 System.out.println("1.排序前陣列:" + Arrays.deepToString(array)); // 第一次排序 System.out.println("2.第一次排序-------------------------------start"); sort_2(array,array.length); System.out.println("3.第一次排序後陣列:" + Arrays.deepToString(array)); // 第二次排序 System.out.println("4.第二次排序-------------------------------start"); sort_2(array,array.length); System.out.println("5.第二次排序後陣列:" + Arrays.deepToString(array)); }
測試結果:
4.討論分享
#考考你答案: 1.你知道氣泡排序的核心思想嗎? 1.1.假設待排序序列有n個元素 1.2.整個排序過程中,需要n次冒泡 1.3.每一次冒泡過程中,依次比較交換相鄰兩個元素 1.4.一次冒泡結束,都會有一個元素到達指定的位置 2.你能用java實現氣泡排序嗎? 2.1.參考【3.2】節案例實現 3.你能寫出更優秀的氣泡排序程式碼嗎? 3.1.結合氣泡排序演算法的核心思想:n個元素、n次冒泡,每一次冒泡依次比較交換相鄰的兩個元素 3.2.如果在某一次冒泡中,沒有發生元素交換 3.3.說明待排序序列已經有序,不需要再進行冒泡下去
&n