Java實現:排序演算法--時間複雜度為O(n² )

Hubbert_Xu發表於2018-03-07

為什麼要學習時間複雜度為O(n² )?


1.基礎
2.編碼簡單,易於實現,是一些簡單情景的首選

3.在一些特殊情況下,簡單的排序演算法更有效

4.簡單的排序演算法思想衍生出複雜的排序演算法


1.選擇排序

選擇排序,就是每一次都篩選出最小(或最大)的數字,然後放在每一次排序的第幾個位置。

比如第一次排序,找出最小(或最大)的元素,放在第一個位置,第二次排序,找出最小(或最大)的元素,放在第二個位置.....

順序從小到大排序:

首先選出最小元素:“1”


然後將這個“1”和第一個位置進行交換:


此時的“1”是在最終位置了,可以不去理會了。

接著找出剩餘還沒排序的最小元素:“2”


跟第二個位置進行交換:


以此類推進行排序,這種排序就是選擇排序了。

public class SelectionSort {
	public static void swap(Object[]arr , int i , int j){
		Object obj = arr[i];
		arr[i] = arr[j];
		arr[j] = obj;
	}
	
	public static void sort_asc(Comparable[] arr){
		int n = arr.length -1;
		for( int i = 0 ; i <= n ; i++ ){
			for( int j = i+1 ; j <= n ; j++ ){
				if( arr[j].compareTo(arr[i]) < 0 ){
					swap(arr,i,j);
				}
			}
		}
	}
	
	public static void main(String[] args) {
		Integer [] arr = {10,3,5,2,4,1,8,6,7,9};
		sort_asc(arr);
		for( int  i : arr ){
			System.out.print(i + " ");
		}
	}
}

2.插入排序

插入排序,從前(或往後),第n次排序,則對n個元素進行排序。

例如第一次排序,對於“8”來說,已經排序完畢了。


第二次排序,對於“6”來說,要放到前面合適的位置。




比“8”還要小,放在前面的位置。

第三次排序,對於“2”來說,要放在前面3個元素合適的位置。


“2”比“8”小。


“2”比“6”小,繼續交換一次位置。


進行多次的資料交換

這樣前面3個元素就排序完成了。以此類推排序完成......


public class InsertionSort {
	public static void swap(Object[]arr , int i , int j){
		Object obj = arr[i];
		arr[i] = arr[j];
		arr[j] = obj;
	}
	
	public static void sort_asc(Comparable[]arr){
		int n = arr.length - 1;
		for( int i = 1 ; i <= n ; i++ ) {
			
			//尋找元素arr[i]適合插入的位置
			for( int j = i ; j > 0 ; j--) {
				if( arr[j].compareTo(arr[j-1]) < 0 ) {
					swap(arr,j,j-1);
				} else {
					//如果不小於,則已排序完成,可以跳出當前迴圈,進行下一個迴圈
					break;
				}
			}
		}
	}
	
	public static void main(String[] args) {
		Integer [] arr = {10,3,5,2,4,1,8,6,7,9};
		sort_asc(arr);
		for( int  i : arr ){
			System.out.print(i + " ");
		}
	}
}

對於比較插入排序和選擇排序。

在插入排序中:

public static void InsertSort(Comparable[]arr){
		int n = arr.length - 1;
		for( int i = 1 ; i <= n ; i++ ) {
			
			//尋找元素arr[i]適合插入的位置
			for( int j = i ; j > 0 ; j--) {
				if( arr[j].compareTo(arr[j-1]) < 0 ) {
					swap(arr,j,j-1);
				} else {
					//如果不小於,則已排序完成,可以跳出當前迴圈,進行下一個迴圈
					break;
				}
			}
		}
	}

有一個break操作,能夠節省一些時間。

所以理應來說比較於“選擇排序”來說,要快的很多

但是其實不然,選擇排序只是每一次進行一次資料的交換操作(swap)

插入排序在每一次的排序中要進行多次的資料交換操作(swap裡面有3次賦值的操作),所以對於一個break語句來說,其實並沒有快的多少。

所以能不能改變一下演算法,讓插入排序也能只進行一次資料交換呢?答案是完全可以的

改進的插入排序:

第一次排序“8”還是在那裡:


在第二次排序,“6”的位置的時候,不像之前那樣冒然交換了。

而是先把“6”複製一份,儲存起來。


然後看“6”是否合適放在當前位置。

那麼判斷是否合適放在當前位置,比較這兩位數的大小,“6”比“8”小,所以不能放在當前位置。


所以把“8”向後移動一個位置,直接在當前位置進行賦值,而不是交換


然後再來考察“6”,是否能放在前一個位置:


此時的位置是第一個,所以不用比較了:



接下來看”2“,直接複製一個副本:


因為 “2” 比前面 “8”小,所以“2”不能放在這個位置,此時“8”賦值到“2”這個位置:


然後再次判斷 “2”是不是應該放在之前“8”這個位置,跟前面的“6”比較,不是:

所以把“6”賦值到這個位置:


然後再來看 “2”是不是應該放在原來 “6”放的位置:


因為是第0個位置:所以直接賦值就好了:


依次類推,不用一次排序進行多次資料的交換,而是變成資料的賦值操作。

程式碼如下:

public static void changeSort(Integer [] arr){
		for( int i = 1 ; i < arr.length ; i++ ){
			//尋找元素arr[i]適合的插入位置
			int temp = arr[i];

			//j儲存元素temp應該插入的位置
			int j;
			for( j = i ; j > 0 && arr[j-1] > temp ; j-- ){
				arr[j] = arr[j-1];
			}
			arr[j] = temp;
		}
	}

4.氣泡排序

顧名思義:像冒泡泡一樣上升。

對於一個陣列,有n個元素。

第一輪排序下來,把最大(或最小)的資料給篩選出來,放在陣列的最右(或最左),

然後進行第二輪排序,找出第二大(或次小)的資料,放在陣列的[n-2](或[1]),

....

依次類推,進行第n輪排序,資料便以排序完成。

所以氣泡排序需要兩個迴圈:

public class BubbleSort {
	public static void swap(Object[]arr , int i , int j){
		Object obj = arr[i];
		arr[i] = arr[j];
		arr[j] = obj;
	}
	
	public static void sort_asc(Comparable[]arr){
		for(int i = arr.length-1 ; i >= 0 ; i-- ){
			for( int j = i-1 ; j >= 0 ; j-- ){
				if(arr[i].compareTo(arr[j]) < 0){
					swap(arr,i,j);
				}
			}
		}
	}
	
	public static void main(String[] args) {
		Integer [] arr = {10,3,5,2,4,1,8,6,7,9};
		sort_asc(arr);
		for( int  i : arr ){
			System.out.print(i + " ");
		}
	}

}

相關文章