冒泡排序(Bubble Sort),是一種計算機科學領域的較簡單的排序算法。
它重複地走訪過要排序的元素列,依次比較兩個相鄰的元素,若是順序(如從大到小、首字母從Z到A)錯誤就把他們交換過來。走訪元素的工做是重複地進行直到沒有相鄰元素須要交換,也就是說該元素列已經排序完成。
這個算法的名字由來是由於越小的元素會經由交換慢慢「浮」到數列的頂端(升序或降序排列),就如同碳酸飲料中二氧化碳的氣泡最終會上浮到頂端同樣,故名「冒泡排序」。算法
public void Sort1(int[] num) { Console.WriteLine("方法1:"); var count1 = 0; var count2 = 0; for (var i = 0; i < num.Length - 1; i++) { for (var j = i + 1; j < num.Length; j++) { count1++; if (num[i] <= num[j]) continue; count2++; var temp = num[j]; num[j] = num[i]; num[i] = temp; } } Console.WriteLine($"結果:{string.Join(",", num)};循環次數:{count1};數據交換次數:{count2}"); }
以上內容,除了代碼所有來自網絡,盜圖請諒解,由於作這麼個圖是在太難了後端
經過代碼,咱們發現,冒泡算法的運行次數其實應該是 (n-1)+(n-2)+(n-3)+...,等於 n(n-1)/2 次,因此得出結論,冒泡算的時間複雜度是O(n^2)數組
可是,上面介紹不是說有最好的狀況下(數據自己就是從小到大排列)時間複雜度是 O(n)嗎?網絡
其實,上面的圖片介紹和實際算法都存在一些問題,是有能夠優化的地方的。測試
從文字角度上說,既然叫冒泡算法,咱們想一想水裏氣泡的形態,通常都是從底部升起到水面,因此,爲了更符合實際狀況,咱們的比較工做應該從數組的尾部開始,把最小(從小到大)的元素慢慢移動到數組的最前的位置。優化
因此這裏先修改一版代碼,把比較的順序倒過來,不是把最大的日後移,而是把最小的往前移code
public void Sort2(int[] numbers) { Console.WriteLine("方法2:標準冒泡算法排序"); var count1 = 0; var count2 = 0; for (var i = 0; i < numbers.Length; i++) { for (var j = numbers.Length - 1; j > i; j--) { count1++; if (numbers[j] >= numbers[j - 1]) continue; count2++; var temp = numbers[j - 1]; numbers[j-1] = numbers[j]; numbers[j] = temp; } } Console.WriteLine($"結果:{string.Join(",", numbers)};循環次數:{count1};數據交換次數:{count2}"); }
運行一下,咱們會發現,調整後的代碼循環的次數和效率跟初版如出一轍,確定的啊,由於中心思路沒變,只是把冒泡的方向倒換了一下而已。blog
下面,咱們考慮這樣一個數組:[1,0,2,3,5,4],使用第二版的代碼:排序
第一次循環結束,數組結果爲 [0,1,2,3,4,5],很神奇有沒有,咱們不只把最小的0冒泡到第一位,順帶的最後兩個元素也進行了排序。而後目測一下,數組已是正序排列了,也就是說這是咱們應該能夠返回了有木有?圖片
這也就是爲何要從尾部往前冒泡的緣由。好了,能夠優化的點出現了,接下來,請看第三版代碼
public void Sort3(int[] numbers) { Console.WriteLine("方法2:優化版冒泡算法排序"); var count1 = 0; var count2 = 0; // 剩下的數據是否須要繼續排序標誌 // 由於冒泡排序是從後端兩兩交換,因此在某種時間點上,後端數據可能已是排列好的數據 // 如1,0,2,3,4這種狀況,第一次循環後,把0交換到最前,結果爲0,1,2,3,4 // 繼續循環1,2,3,4,若是沒有進行數據交換操做,說明已是排序好的,就不必繼續了,直接跳出便可 var continueFlag = true; for (var i = 0; i < numbers.Length && continueFlag; i++) { for (var j = numbers.Length - 1; j > i; j--) { count1++; if (numbers[j] < numbers[j - 1]) { count2++; var temp = numbers[j - 1]; numbers[j - 1] = numbers[j]; numbers[j] = temp; // 一旦發生數據交換的操做,說明後面的數據並無排列好,這時須要繼續循環 continueFlag = true; } else { // 沒有發生數據交換,說明數據已是排列好的,這時能夠跳出循環了 continueFlag = false; } } } Console.WriteLine($"結果:{string.Join(",", numbers)};循環次數:{count1};數據交換次數:{count2}"); }
下面,來進行一個測試:
var bubble = new BubbleSortSample(); var numbers = new int[] {3, 1, 2, 4, 6, 9, 5}; bubble.Sort1(numbers); Console.WriteLine(); numbers = new int[] { 3, 1, 2, 4, 6, 9, 5 }; bubble.Sort2(numbers); Console.WriteLine(); numbers = new int[] { 3, 1, 2, 4, 6, 9, 5 }; // 第一次 1,3,2,4,5,6,9,發生了交換,循環了6次 // 第二次 1,2,3,4,5,6,9,發生了交換,循環了5次 // 第三次 檢查了一圈,循環了4次,沒有發生交換,跳出 bubble.Sort3(numbers);
下面是運行結果:
冒泡排序是一種比較基礎並且你們比較熟悉的算法,之前老是知其然而沒有深究過,網上不少例子也是如初版代碼同樣簡單實現演示一下了事,殊不知原來這麼基礎的算法也有這麼多的門道在其中,因此說學海無涯...省略一萬五千字雞湯