1. 程式人生 > >Java資料結構之 常見排序

Java資料結構之 常見排序

一. 氣泡排序

思想:大的沉下去,小的升上來。

public class TestDemo1 {
	//氣泡排序優化
	public static void bubbleBetterSort(int[] array){
		int tmp = 0;//交換用於存值
		int count = 0;//用於判斷陣列是否有序
		for(int i = 0;i <array.length-1;i++){
			for(int j = 0;j < array.length-i-1;j++){
				if(array[j] > array[j+1]){
					tmp = array[j];
					array[j] = array[j+1];
					array[j+1] = tmp;
					count++;//如果交換過,count自增
				}
			}
			if(count == 0)//當前陣列已經有序
				break;
		}
		System.out.println(Arrays.toString(array));
	}
這裡寫的是已經優化好的氣泡排序,如果陣列 本身有序,就不需要進行那麼多趟的比較。因此用count來記錄第一趟裡if執行的次數,一次都沒有執行,說明陣列本身有序。

二. 選擇排序

思想:每一次從待排序的資料元素中選出最小(或最大)的一個元素,存放在序列的起始位置,直到全部待排序的資料元素排完。

public static void selectSort(int[] array){
		int min=0;//找最小值的下標
		int tmp;//用於交換
		int j;
		for(int i = 0;i < array.length-1;i++){
			min = i;
			for(j = i+1;j < array.length;j++){//從i+1位置開始找最小值
				if(array[j]<array[min]){
					min = j;
				}
			}
			//將最小值放到本趟比較的最前面
			tmp = array[i];
			array[i] = array[min];
			array[min] = tmp;
		}
		System.out.println(Arrays.toString(array));
	}

三. 直接插入排序

思想:假設待排序的資料是陣列A[1….n]。初始時,A[1]自成1個有序區,無序區為A[2….n]。在排序的過程中,依次將A[i] (i=2,3,….,n)從後往前插入到前面已排好序的子陣列A[1,…,i-1]中的適當位置,當所有的A[i] 插入完畢,陣列A中就包含了已排好序的輸出序列。


public class TestDemo3 {
	public static void insrtSort(int[] array){
		int tmp = 0;
		int j;
		for(int i = 1;i < array.length;i++){//從i號位值開始
			tmp = array[i];
			for(j = i-1;j >= 0;j--){//找合適的位置進行插入
				if(array[j] > tmp){
					array[j+1] = array[j];
				}
				//在迴圈中找到了比tmp小的第一個元素跳出迴圈
				else{
					break;
				}
			}
			//把tmp賦值到最小值的後面
			array[j+1] = tmp;
		}
		System.out.println(Arrays.toString(array));
	}

四. 希爾排序(shell排序)

思想:是直接插入排序的一種更高效的版本,又稱“縮小增量排序”。希爾排序是把記錄按下標的一定增量分組,組內採用直接插入排序;隨著增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個陣列恰被分成一組,演算法便終止。

增量序列如何選取?

①互為質數  ②最後一個增量為一

一般情況下選取[5,3,1]作為增量序列

接下來以圖來進行希爾排序

原始序列:


①將序列分為5組


如圖,相同顏色的線為同一組。接下來組內進行直接插入排序。

排序後結果:


②:第一次排序後的序列分為三組

排序後:


③最後對這個序列整個進行直接插入排序

排序後結果:


接下來實現shell排序

public static void shell(int[] array,int gap){//gap表示分成幾組
		int tmp = 0;//存放要交換的資料元素
		for(int i = gap;i < array.length;i++){//第一次要排序的元素是gap,不能寫成i=i+gap
			tmp = array[i];
			int j = 0;
			for(j = i-gap;j >= 0;j -= gap){
				if(array[j] > tmp){
					array[j+gap] = array[j];
				}else{
					break;
				}
			}
			array[j+gap] = tmp;
		}
	}
	public static void shellSort(int[] array){
		int[] d = {5,3,1};//增量序列
		for(int i = 0;i < d.length;i++){
			shell(array,d[i]);//每一次分的組進行直接插入排序
		}
	}

五. 快速排序

思想:①在陣列中找一個數作為基準(我們以零號陣列下標作為第一個基準)

          ②比基準小的放在它的左邊,比基準大的放在它的右邊

          ③對左右區域分別重複第二步操作,直至左右區域都只剩下一個元素。

1.快速排序的遞迴實現

原始序列:


①:從high開始往前找,找到第一個比基準21小的,將high指向的4放在low指向的地方。執行後如圖:


②:從low往後找,找到第一個比基準21大的,將low指向的32放在high指向的位置。執行後如圖:


③:high繼續往前找比low小的,執行如圖:


因為high和low相遇,此時將low(或high)位置的數用基準替代。執行後如圖


現在發現基準21左邊都比它小,右邊都比它大。這樣第一趟快速排序就結束了。

之後再分別對左右兩邊用與上述相同的方法排序,就會得到一個有序的序列了。

程式碼實現:

public static int partition(int[] array,int start,int end){//返回基準的下標
		int low = start;
		int high = end;
		int tmp = array[low];
		while(low < high){
			while(low < high&&array[high] >= tmp){//從後往前找比基準小的後退出迴圈
				--high;
			}
			if(low >= high){//判斷low與high是否相遇
				break;
			}else{
				array[low] = array[high];
			}
			while(low < high&&array[low] <= tmp){//從前往後找比基準大的退出迴圈
				++low;
			}
			if(low >= high){
				break;
			}else{
				array[high] = array[low];
			}
		}
		array[low] = tmp;
		return low;
	}
	public static void quick(int[] array,int start,int end){
		int par = partition(array, start, end);
		if(par > start+1){//左半部分在進行快排
			quick(array, start, par-1);
		}
		if(par < end - 1){//右半部分進行快排
			quick(array, par+1, end);
		}
	}
	public static void quickSort(int[] array){
		int low = 0;
		int high = array.length - 1;
		quick(array, low, high);
	}

2.非遞迴實現

用棧來實現

public static int partition(int[] array,int start,int end){//返回基準的下標
		int low = start;
		int high = end;
		int tmp = array[low];
		while(low < high){
			while(low < high&&array[high] >= tmp){
				--high;
			}
			if(low >= high){
				break;
			}else{
				array[low] = array[high];
			}
			while(low < high&&array[low] <= tmp){
				++low;
			}
			if(low >= high){
				break;
			}else{
				array[high] = array[low];
			}
		}
		array[low] = tmp;
		return low;
	}
	public static void quickSort(int[] array){
		int[] stack = new int[array.length];
		int top = 0;
		int low = 0;
		int high = array.length-1;
		//入棧
		int par = partition(array,low,high);
		if(par > low+1){
			stack[top++] = low;
			stack[top++] = par-1;
		}
		if(par < high-1){
			stack[top++] = par+1;
			stack[top++] = high;
		}
		//出棧
		while(top > 0){
			high = stack[--top];
			low = stack[--top];
			
			par = partition(array, low, high);
			
			if(par >low+1){
				stack[top++] = low;
				stack[top++] = par-1;
			}
			if(par < high-1){
				stack[top++] = par+1;
				stack[top++] = high;
			}
		}
	}

以上排序的複雜度與穩定性:

時間複雜度穩定性
氣泡排序O(n²)穩定
選擇排序O(n²)不穩定
直接插入排序O(n²)(最好O(n))穩定
希爾排序O(n^1.3)最壞(O(n²))不穩定
快速排序O(nlogn)(最壞O(n²))不穩定