1. 程式人生 > >八大內部排序以及穩定性

八大內部排序以及穩定性

穩定性的定義:如果佇列中存在相等的兩個數字,如果數字在排序過程中不發生變化,則是穩定的排序,如果發生了變化則是不穩定的排序。

回憶的過程:以後看過,長時間沒寫了,從頭寫一遍,跟以前寫的比較比較看看自己有什麼改進。

初級菜鳥,如果問題望指出,多多溝通,有利於成長。

1. 交換排序:氣泡排序、快排排序

(1) 冒泡思路:每次迴圈大的往下沉,每次迴圈迴圈的長度-1。swap方法為自定義方法交換在部落格最後面

<span style="font-size:18px;">/*
	 * 氣泡排序 交換排序顧名思義,假如某個位置的資料是確定的,尋找相應條件的資料進行交換, 例如:陣列{ 49, 38, 65, 97, 76, 13,
	 * 27, 49 }。我採用從小到大的排序方式 假如49就是已經確定位置的資料,從資料38開始遍歷,38小於49,交換位置。以此類推。
	 * 
	 */
	public static int[] bubbleSort(int arr[]) {
		int le = arr.length - 1;
		while (le >= 0) {
			int inter = arr[0];
			int index = 0;
			for (int j = 1; j <= le; j++) {
				if (arr[j] < inter)
					/*
					 * 對於穩定性的概念也不是絕對的,如果此處的條件是arr[j] <= inter
					 * 那麼這個氣泡排序就是不穩定的排序,所有有的時候穩定是相對的跟自己定義的條件有關係的
					 */
					swap(arr, index, j);
				else
					inter = arr[j];
				index = j;
			}
			le--;
		}

		return arr;
	}</span>
(2) 快排思路:設定一個標誌位,從前面找比標誌位小的從後面找比標誌位大的,分治遞迴。
<span style="font-size:18px;">public static void quickSort(int arr[], int from, int to) {
		if (from >= to)
			return;
		// 標誌位,將比標誌位小的放到某個數的前面,將比標誌位大的放到標誌位的後面,遞迴解決--分治法
		int temp = arr[from];
		// 每次交換的位置
		int index = from;
		int start = from;
		int end = to;
		while (start < end) {
			// 從後往前找比標誌位元素小的,
			while (start < end && arr[end] >= temp)
				end--;
			if (arr[end] < temp) {
				arr[index] = arr[end];
				index = end;
			}

			while (start < end && arr[start] < temp)
				start++;
			if (arr[start] > temp) {
				arr[index] = arr[start];
				index = start;
			}
		}
		// 在最後一個交換的位置新增標誌位,標誌位可以將問題分解為兩個不同的問題
		arr[index] = temp;
		quickSort(arr, from, index - 1);
		quickSort(arr, index + 1, to);

	}</span>

2.  插入排序:插入排序、希爾排序。

(1)  插入排序:設定一個標誌位,假設第一元素是排好序的,與第下一個元素進行比較,大與直接進行下一個,小於需要找到該元素插入的恰當的位置。

<span style="font-size:18px;">public static void insertSort(int arr[]) {
		int le = arr.length - 1;
		// 元素比較的位置
		int start = 1;
		int temp = arr[0];
		while (start <= le) {
			if (arr[start] < temp) {
				int index = start - 1;
				int inNum = arr[start];
				/*
				 * 移動元素: 38,49,,65, 76, 97,13, 27, 49
				 * 例如如果start在13位置,需要找到13的恰當的位置,與前一個元素(97)進行比較,如果前一個元素(97)>13,
				 * 則將這個元素往後移動,找到13位置的過程也就是不斷的將元素往後推一個位置,如果小於則直接進行下一輪的迴圈,即start++
				 * ;
				 */
				while (index >= 0 && inNum <= arr[index]) {
					arr[index + 1] = arr[index];
					index--;
				}
				if (index == -1)
					arr[0] = inNum;
				else
					arr[index + 1] = inNum;
			} else
				temp = arr[start];
			start++;
		}
	}</span>

(2) 希爾排序:主要就是取步長,比較步長之間數字的大小

<span style="font-size:18px;">	public static void shellSort(int arr[]) {
		/*
		 * 希爾本質上和插入排序是一樣的,但是它比較的不是相鄰元素的大小而是比較步長之間的元素的大小,說不太清楚,舉個例子: 陣列49, 38,
		 * 65, 97, 76, 13, 27, 49 。如果步長為3,則39,97,27一組之間比較,
		 * 38,76,49之間比較,等等。我自己理解哈:這樣做的好處可以把相對小的數前移,相對大的數後移,這樣就避免了插入排序中插入13的時候,
		 * 移動那麼多數。
		 */
		int step = arr.length / 2;
		int le = arr.length - 1;
		int m = 0;
		// 元素比較的位置
		while (step > 0) {
			int temp = arr[0];
			int start = step;
			while (start <= le) {
				temp = arr[start - step];
				if (arr[start] < temp) {
					int index = start - step;
					int inNum = arr[start];
					/*
					 * 移動元素: 38,49,,65, 76, 97,13, 27, 49
					 * 例如如果start在13位置,需要找到13的恰當的位置,與前一個元素(97)進行比較,如果前一個元素(97)>
					 * 13, 則將這個元素往後移動,找到13位置的過程也就是不斷的將元素往後推一個位置,如果小於則直接進行下一輪的迴圈,
					 * 即start++ ;
					 */
					while (index >= 0 && inNum <= arr[index]) {
						arr[index + step] = arr[index];
						index -= step;
					}
					arr[index + step] = inNum;
				}

				start++;
			}
			step--;
		}
	}</span>

3.選擇排序

(1) 選擇排序:每次迴圈選擇陣列中最小的或者最大的。

<span style="font-size:18px;">public static void selectSort(int arr[]) {
		for (int i = 0; i < arr.length; i++) {
			//每次迴圈之前I之前的元素是已經被排序好的,i之後是未排序的,假設當前元素I是最小的,在I之後去尋找比I小的。
			int min = arr[i];
			int index = i;
			for (int j = i + 1; j < arr.length; j++) {
				if (arr[j] < min) {
					min = arr[j];
					index = j;
				}
			}
			swap(arr, i, index);
		}
	}</span>

(2) 堆排序:構建大頂堆或者小頂堆,交換第一元素和最後元素,繼續構建堆的過程。

<span style="font-size:18px;">/*
	 * 從i = arr.length / 2
	 * -1(-1是因為陣列的下標從0開始的)一半元素開始進行操作的,所以首先要知道當前節點有一個節點還是有兩個節點 這個方法是完成構建小頂堆
	 */
	public static int[] heapSort(int arr[], int i, int le) {

		while (i >= 0 && 2 * i + 1 <= le) {
			// 右節點
			int r = 2 * i + 2;
			// 左節點
			int l = 2 * i + 1;

			int min = 0;
			int minIndex = 0;
			/*
			 * 判斷當前節點是有一個孩子節點還是兩個孩子節點,第一次(step=3)判斷的時候
			 * 當r=le時說明有兩個孩子節點,否則有個一個孩子節點,其他的次數(step=2,1,0)均有兩個節點
			 */
			if (r <= le) {
				// 取孩子節點中最小的節點
				if (arr[r] > arr[l]) {
					min = arr[l];
					minIndex = l;
				} else {
					min = arr[r];
					minIndex = r;
				}
				// 判斷最小的節點和父節點大小,
				if (min < arr[i]) {
					swap(arr, i, minIndex);
					/*
					 * 在交換之後,元素的位置發生變化了,在i位置的元素不需要做什麼,因為下一個進行操作的是i-1,依次往下進行,
					 * 它會自動的檢測到他的父節點是不是小於它的兩個子節點,但是它的子節點並不知道交換之後滿足不滿足這個要求,
					 * 所以需要檢查一下-ps:這個你按照陣列把堆畫出來,然後自己交換13和49的時候你就會發現了,父節點49子節點65,
					 * 27
					 */
					heapSort(arr, minIndex, le);
				}
			} else {
				/*
				 * 當前節點只有一個孩子節點時,只有在第一次執行的時候可能發生,所以直接比較即可
				 */
				if (arr[i] > arr[l])
					swap(arr, i, l);
			}
			i--;
		}
		// heapSortRemove(arr, 0, le);
		return arr;
	}

	/*
	 * 小頂堆排序,每次將最小的交換到最後一個節點,切長度-1,然後在進行構建小頂堆
	 */
	public static int[] heapSortRemove(int arr[], int le) {

		while (le >= 0) {
			int temp = arr[0];
			arr[0] = arr[le];
			arr[le] = temp;
			le--;
			/*
			 * 呼叫heapSort方法時,因為它只換了最後一個元素和第一個元素,所以檢查第一元素即可
			 */
			heapSort(arr, 0, le);
		}
		return arr;
	}</span>

注意:大頂堆在每一次交換的時候將最大的交換到最後一個位置,所以大頂堆的結果陣列是從小到大排列的。 小頂堆則是恰恰相反,從大到小。


4.歸併排序

思路:主要應用分治法,將問題分解成小的問題,進行排序,然後合併進行排序。

<span style="font-size:18px;">/*
	 * 將陣列分成兩部分,(0,3)、(4,7)-再分成兩部分(0,1)(2-3)等,直到(0,0)停止 ,之後合併(0,1),(0,3),(0,7)。
	 */

	public static void mergeSort(int arr[], int from, int to, int temp[]) {
		if (from < to) {
			int mid = (from + to) / 2;
			mergeSort(arr, from, mid, temp);
			mergeSort(arr, mid + 1, to, temp);
			mergearray(arr, from, mid, to, temp);
		}
	}

	/*
	 * 如果是兩個元素(0,1),將兩個元素兩個元素排序,,如果是4個(0,4)元素將四個元素排序,temp作為中間陣列,將排序的值,暫時儲存下來,
	 * 之後儲存到arr中。
	 */
	public static void mergearray(int arr[], int from, int mid, int to, int temp[]) {
		int i = from;
		int inter = mid + 1;
		int k = 0;
		while (from <= mid && inter <= to) {
			if (arr[from] < arr[inter])
				temp[k++] = arr[from++];
			else
				temp[k++] = arr[inter++];
		}
		while (from <= mid)
			temp[k++] = arr[from++];
		while (inter <= to)
			temp[k++] = arr[inter++];

		for (int j = 0; j < k; j++) {
			arr[j + i] = temp[j];
		}
	}
</span>

5.交換方法swap

<span style="font-size:18px;">public static void swap(int arr[], int i, int j) {
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}</span>

6.主函式

<span style="font-size:18px;">public static void main(String[] args) {
		int arr[] = { 49, 38, 65, 97, 76, 13, 27, 49, 24 };

		// 氣泡排序
		// int a[] = bubbleSort(arr);
		// 快排排序
		// quickSort(arr, 0, a,rr.length - 1);
		// 插入排序
		// insertSort(arr);
		// 希爾排序
		// shellSort(arr);
		// 選擇排序
		// selectSort(arr);
		// 堆排序
		// arr = heapSort(arr, arr.length / 2 - 1, arr.length - 1);
		// arr = heapSortRemove(arr, arr.length - 1);
		// 歸併排序
		int temp[] = new int[arr.length];
		mergeSort(arr, 0, arr.length - 1, temp);
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
	}</span>


6. 各個排序演算法的穩定性

 轉載位置:http://www.cnblogs.com/pangxiaodong/archive/2011/08/19/2145260.html

基數排序我並沒有寫,這個我沒研究,有空會有後續的。