數組中涉及的常見算法: 4、數組元素的排序算法(冒泡排序、快速排序)

數組元素的排序算法


排序的定義:

假設含有n個記錄的序列爲{R1,R2,…,Rn},其相應的關鍵字序列爲{K1,K2,…,Kn}。將這些記錄重新排序爲{Ri1,Ri2,…,Rin},使得相應的關鍵字值滿足條Ki1<=Ki2<=…<=Kin,這樣的一種操作稱爲排序。

  • 通常來說,排序的目的是快速查找

衡量排序算法的優劣:

1.時間複雜度:分析關鍵字的比較次數和記錄的移動次數
2.空間複雜度:分析排序算法中需要多少輔助內存
3.穩定性:若兩個記錄A和B的關鍵字值相等,但排序後A、B的先後次序保持不變,則稱這種排序算法是穩定的。


排序算法分類:內部排序和外部排序。

  • 內部排序:整個排序過程不需要藉助於外部存儲器(如磁盤等),所有排序操作都在內存中完成。

  • 外部排序:參與排序的數據非常多,數據量非常大,計算機無法把整個排序過程放在內存中完成,必須藉助於外部存儲器(如磁盤)。外部排序最常見的是多路歸併排序。可以認爲外部排序是由多次內部排序組成。


十大內部排序算法

  1. 選擇排序
    直接選擇排序、堆排序
  2. 交換排序
    冒泡排序、快速排序(這兩個要求:熟悉)
  3. 插入排序
    直接插入排序、折半插入排序、Shell排序
  4. 歸併排序
  5. 桶式排序
  6. 基數排序

算法的五大特徵

輸入(Input) 有0個或多個輸入數據,這些輸入必須有清楚的描述和定義
輸出(Output) 至少有1個或多個輸出結果,不可以沒有輸出結果
有窮性 (有限性,Finiteness) 算法在有限的步驟之後會自動結束而不會無限循環,並且每一個步驟可以在可接受的時間內完成
確定性(明確性,Definiteness) 算法中的每一步都有確定的含義,不會出現二義性
可行性(有效性,Effectiveness) 算法的每一步都是清楚且可行的,能讓用戶用紙筆計算而求出答案

說明:滿足確定性的算法也稱爲:確定性算法。現在人們也關注更廣泛的概念,例如考慮各種非確定性的算法,如並行算法、概率算法等。另外,人們也關注並不要求終止的計算描述,這種描述有時被稱爲過程(procedure)。


冒泡排序

介紹:冒泡排序的原理非常簡單,它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。

排序思想:

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

public class BubbleSortTest {
	public static void main(String[] args) {
		
		int[] arr = new int[]{43,32,76,-98,0,64,33,-21,32,99};

		for(int i = 0;i < arr.length - 1;i++){
			
			for(int j = 0;j < arr.length - 1 - i;j++){
				
				if(arr[j] > arr[j + 1]){
					int temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}
				
			}
			
		}
		
		for(int i = 0;i < arr.length;i++){
			System.out.print(arr[i] + "\t");
		}
		
	}
}

練習一:

使用冒泡排序,實現如下的數組從小到大排序。
int[] arr = new int[]{34,5,22,-98,6,-76,0,-3};

class BubbleSortTest {
	public static void main(String[] args) {
		int[] arr = new int[] { 34, 5, 22, -98, 6, -76, 0, -3 };

		System.out.print("原先的數組:");
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + "\t");
		}

		System.out.println();
		for (int i = 0; i < arr.length - 1; i++) {
			for (int j = 0; j < arr.length - 1 - i; j++) {
				if (arr[j] > arr[j + 1]) {
					int temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}
			}
		}
		System.out.print("經冒泡後的:");
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + "\t");
		}
	}
}

快排時間複雜度:O(nlogn)
冒泡時間複雜度:O(n^2)
堆排序、歸併排序


快速排序

介紹:快速排序通常明顯比同爲O(nlogn)的其他算法更快,因此常被採用,而且快排採用了分治法的思想,所以在很多筆試面試中能經常看到快排的影子。可見掌握快排的重要性。

快速排序(Quick Sort)由圖靈獎獲得者Tony Hoare發明,被列爲20世紀十大算法之一,是迄今爲止所有內排序算法中速度最快的一種。冒泡排序的升級版,交換排序的一種。快速排序的時間複雜度爲O(nlog(n))。

排序思想:

1. 從數列中挑出一個元素,稱爲"基準"(pivot),
2. 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分區結束之後,該基準就處於數列的中間位置。這個稱爲分區(partition)操作。
3. 遞歸地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。
4. 遞歸的最底部情形,是數列的大小是零或一,也就是永遠都已經被排序好了。雖然一直遞歸下去,但是這個算法總會結束,因爲在每次的迭代(iteration)中,它至少會把一個元素擺到它最後的位置去。

/**
 * 快速排序
 * 通過一趟排序將待排序記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分關鍵字小,
 * 則分別對這兩部分繼續進行排序,直到整個序列有序。

 */
public class QuickSort {
	private static void swap(int[] data, int i, int j) {
		int temp = data[i];
		data[i] = data[j];
		data[j] = temp;
	}

	private static void subSort(int[] data, int start, int end) {
		if (start < end) {//此處爲核心代碼,
			int base = data[start];
			int low = start;
			int high = end + 1;
			while (true) {
				while (low < end && data[++low] - base <= 0)
					;
				while (high > start && data[--high] - base >= 0)
					;
				if (low < high) {
					swap(data, low, high);
				} else {
					break;
				}
			}
			swap(data, start, high);
			
			subSort(data, start, high - 1);//方法自己又調用自己了,稱爲遞歸調用(遞歸方法)
			subSort(data, high + 1, end);
		}
	}
	public static void quickSort(int[] data){
		subSort(data,0,data.length-1);
	}
	
	//舉例
	public static void main(String[] args) {
		int[] data = { 9, -16, 30, 23, -30, -49, 25, 21, 30 };
		System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
		quickSort(data);
		System.out.println("排序之後:\n" + java.util.Arrays.toString(data));
	}
}

排序算法性能對比

在這裏插入圖片描述


各種內部排序方法性能比較

  1. 從平均時間而言:快速排序最佳。但在最壞情況下時間性能不如堆排序和歸併排序。
  2. 從算法簡單性看:由於直接選擇排序、直接插入排序和冒泡排序的算法比較簡單,將其認爲是簡單算法。對於Shell排序、堆排序、快速排序和歸併排序算法,其算法比較複雜,認爲是複雜排序。
  3. 從穩定性看:直接插入排序、冒泡排序和歸併排序時穩定的;而直接選擇排序、快速排序、Shell排序和堆排序是不穩定排序
  4. 從待排序的記錄數n的大小看,n較小時,宜採用簡單排序;而n較大時宜採用改進排序。

排序算法的選擇

(1)若n較小(如n≤50),可採用直接插入或直接選擇排序
當記錄規模較小時,直接插入排序較好;否則因爲直接選擇移動的記錄數少於直接插入,應選直接選擇排序爲宜。

(2)若文件初始狀態基本有序(指正序),則應選用直接插入冒泡或隨機的快速排序爲宜

(3)若n較大,則應採用時間複雜度爲O(nlgn)的排序方法:快速排序、堆排序或歸併排序

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