十大經典排序算法之-冒泡排序

前言

本系列排序包括十大經典排序算法。

  • 使用的語言爲:Java
  • 結構爲: 定義抽象類Sort裏面實現了,交換,大小比較等方法。例如交換兩個值,直接傳入下標就可以了。其他的具體排序的類都繼承抽象類Sort。這樣我們就能專注於算法本身。
/*
	 * 返回值等於0,代表 array[i1] == array[i2]
	 * 返回值小於0,代表 array[i1] < array[i2]
	 * 返回值大於0,代表 array[i1] > array[i2]
	 */
	protected int cmp(int i1, int i2) {
		return array[i1].compareTo(array[i2]);
	}
	
	protected int cmp(T v1, T v2) {
		return v1.compareTo(v2);
	}
	
	protected void swap(int i1, int i2) {
		T tmp = array[i1];
		array[i1] = array[i2];
		array[i2] = tmp;
	}

複製代碼

什麼是冒泡排序

  • 冒泡排序(Bubble Sort)是一種計算機科學領域的較簡單的排序算法。
  • 它重複地走訪過要排序的元素列,依次比較兩個相鄰的元素,如果他們的順序(如從大到小、首字母從A到Z)錯誤就把他們交換過來。走訪元素的工作是重複地進行直到沒有相鄰元素需要交換,也就是說該元素列已經排序完成。
  • 這個算法的名字由來是因爲越大的元素會經由交換慢慢“浮”到數列的頂端(升序或降序排列),就如同碳酸飲料中二氧化碳的氣泡最終會上浮到頂端一樣,故名“冒泡排序”。

算法原理

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

 

 

 

算法分析

時間複雜度

  • 若文件的初始狀態是正序的,一趟掃描即可完成排序。所需的關鍵字比較次數C和記錄移動次數M 均達到最小值:C=n-1 , M=0。
  • 所以,冒泡排序最好的時間複雜度爲O(n) 。
  • 若初始文件是反序的,需要進行 n-1 趟排序。每趟排序要進行 n-i 次關鍵字的比較(1≤i≤n-1),且每次比較都必須移動記錄三次來達到交換記錄位置。在這種情況下,比較和移動次數均達到最大值: C = n(n-1)/2 = O(n^2). M = 3n(n-1)/2 = O(n^2).

冒泡排序的最壞時間複雜度爲 O(n^2)。 綜上,因此冒泡排序總的平均時間複雜度爲O(n^2)。

算法穩定性

  • 冒泡排序就是把小的元素往前調或者把大的元素往後調。比較是相鄰的兩個元素比較,交換也發生在這兩個元素之間。所以,如果兩個元素相等,是不會再交換的;如果兩個相等的元素沒有相鄰,那麼即使通過前面的兩兩交換把兩個相鄰起來,這時候也不會交換,所以相同元素的前後順序並沒有改變,所以冒泡排序是一種穩定排序算法。

是否是原地算法

  • 何爲原地算法?
    • 不依賴額外的資源或者依賴少數的額外資源,僅依靠輸出來覆蓋輸入
    • 空間複雜度爲 𝑂(1) 的都可以認爲是原地算法
  • 非原地算法,稱爲 Not-in-place 或者 Out-of-place
  • 冒泡排序屬於 In-place

代碼

代碼一

public class BubbleSort<T extends Comparable<T>> extends Sort<T>  {

	@Override
	protected void sort() {
		for (int end = array.length-1; end>0; end--) {
			for (int begin = 1; begin <= end; begin++) {
				if (cmp(begin, begin-1)<0) {
				//ayyay[begin] 小於 ayyay[begin-1] 就交換
					swap(begin, begin-1);
				}
			}
		}
	}
}

複製代碼

優化

  • 我們知道,每次都是兩兩比較,如果已經拍好順序了。可以提前終止排序
public class BubbleSort1<T extends Comparable<T>> extends Sort<T>  {

	@Override
	protected void sort() {
		// TODO Auto-generated method stub
		for (int end = array.length-1; end >0; end--) {
			boolean isSorted = true; //定義布爾值 isSorted來標記是否有交換
			for (int begin = 1; begin <= end; begin++) {
				//ayyay[begin] 小於 ayyay[begin-1] 就交換
				if (cmp(begin, begin-1)<0) {
					swap(begin, begin-1);
					isSorted = false;
				}
			}
			if (isSorted) {
				//來到這裏,說明沒有交換過。已經是完全有序的了。提前終止排序
				break;
			}
			
		}
	}

}

複製代碼

再次優化

  • 如果序列尾部已經局部有序,可以記錄最後1次交換的位置,減少比較次數
public class BubbleSort2<T extends Comparable<T>> extends Sort<T>   {

	@Override
	protected void sort() {
		// TODO Auto-generated method stub
		for (int end = array.length-1; end >0; end--) {
			int sortedIndex = 1;
			for (int begin = 1; begin <= end; begin++) {
				if (cmp(begin, begin-1)<0) {
					swap(begin, begin-1);
					sortedIndex = begin;
				}
			}
			end = sortedIndex;
			
		}
	}

}

複製代碼

驗證

使用數據源如下

Integer[] array = {7, 3, 5, 8, 6, 7, 4, 5,19,30,40,50};

結果爲:

  • 【BubbleSort】 穩定性:true 耗時:0.0s(0ms) 比較次數:66 交換次數:14

  • 【BubbleSort1】穩定性:true 耗時:0.001s(1ms) 比較次數:51 交換次數:14

  • 【BubbleSort2】穩定性:true 耗時:0.0s(0ms) 比較次數:30 交換次數:14

可以明顯感覺到做了優化之後,比較測試減少了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章