各類排序算法總結(Java代碼實現)

一、   穩定性

穩定性是指待排序的序列中有兩個或者兩個以上相同的項,排序前和排序後,看這些相同的項的相對位置有沒有沒發生變化,如果沒有發生變化,就是穩定的;如果發生變化,就是不穩定的。

二、   排序分類以及複雜度

2.1、      插入類排序

2.1.1、直接插入排序

最壞時間複雜度:O(n^2)

最好時間複雜度:O(n)

平均時間複雜度:O(n^2)

空間複雜度:O(1)

2.1.2、折半插入排序

最壞時間複雜度:O(n^2)

最好時間複雜度:O(n)

平均時間複雜度:O(n^2)

空間複雜度:O(1)

2.1.3、希爾排序

平均時間複雜度:O(nlogn)

空間複雜度:O(1)

2.2、      交換類排序

2.2.1、冒泡排序

最壞時間複雜度:O(n^2)

最好時間複雜度:O(n)

平均時間複雜度:O(n^2)

空間複雜度:O(1)

2.2.2、快速排序

最壞時間複雜度:O(n^2)

最好時間複雜度:O(nlogn)

平均時間複雜度:O(nlogn)

空間複雜度:O(logn)

2.3、      選擇類排序

2.3.1、簡單選擇排序

最壞時間複雜度:O(n^2)

最好時間複雜度:O(n^2)

平均時間複雜度:O(n^2)

空間複雜度:O(1)

2.3.2、堆排序

最壞時間複雜度:O(nlogn)

最好時間複雜度:0(nlogn)

平均時間複雜度:O(nlogn)

空間複雜度:O(1)

2.4、      歸併類排序

2.4.1、二路歸併排序

最壞時間複雜度:O(nlogn)

最好時間複雜度:O(nlogn)

平均時間複雜度:O(nlogn)

空間複雜度:O(n)

2.5、      基數類排序

最壞時間複雜度:O(d(n+rd))

最好時間複雜度:

平均時間複雜度:O(d(n+rd))

空間複雜度:O(rd)

三、   各類排序算法Java代碼

3.1、      直接插入排序

介紹和思想

介紹:插入排序基本操作就是將一個數據插入到已經排好序的有序數據中,從而得到一個新的、個數加一的有序數據,算法適用於少量數據的排序,時間複雜度爲O(n^2)。是穩定的排序方法。

思想:每步將一個待排序的紀錄,按其關鍵碼值的大小插入前面已經排序的文件中適當位置上,直到全部插入完爲止。

算法代碼

public static <T extends Comparable<? super T>> void insertionSort(T[] a) {
		int j;
		for (int i = 1; i < a.length; i++) {
			T tmp = a[i];
			for (j = i; j > 0 && tmp.compareTo(a[j-1])<0; j--) {
				a[j] = a[j-1];
			}
			a[j] = tmp;
		}
	}

3.2、      折半插入排序

介紹和思想

折半插入排序的基本思想和直接插入排序一樣,區別在於尋找插入位置的方法不同,折半插入排序是採用折半查找方法類尋找插入位置的。

算法代碼

public static void binaryInsertSort(int[] a) {
		for(int i = 1;i < a.length;i++) {
			int temp = a[i];
			int low = 0;
			int high = i-1;
			while(low<=high) {
				int mid = (low+high)/2;
				if(temp < a[mid]) {
					high = mid-1;
				}else {
					low = mid+1;
				}
			}
			for(int j = i;j>=low+1;j--) {
				a[j] = a[j-1];
			}
			a[low] = temp;
		}
	}

3.3、      冒泡排序

算法介紹和思想

冒泡排序是通過一系列的“交換”動作完成的,是交換類排序的一種。

首先第一個記錄和第二個記錄比較,如果第一個大,則兩者交換,否則不交換;

然後第二個記錄和第三個記錄比較,如果第二個大,則兩者交換,否則不交換……一直按這種方式進行下去,最終最大的那個記錄被交換到了最後,一趟冒泡排序完成。

然後進行第二趟冒泡排序,對前n-1個記錄進行同樣的操作,使關鍵字次大的記錄被安置到第n-1個記錄的位置上……一直這樣進行下去,直到在一趟排序過程中沒有進行過交換記錄的操作,這也是冒泡排序算法的結束條件

算法代碼
public static <T extends Comparable<? super T>> void bubbleSort(T[] a) {
		int i,j,flag;
		T temp;
		for(i=a.length-1;i>0;i--) {//最多進行a.length-1趟排序
			flag = 0;
			for(j=0;j<i;j++) {////對當前無序區間a[0......i]進行排序
				if(a[j].compareTo(a[j+1])>0) {//大值交換到後面
					temp = a[j+1];
					a[j+1] = a[j];
					a[j] = temp;
					flag = 1;
				}
			}
			if(flag == 0) {//結束條件:某一趟排序是否有交換
				return;
			}
		}
	}

3.4、      快速排序

算法介紹和思想

快速排序是對冒泡排序的一種改進,它也是“交換”類排序的一種。它的基本思想是,通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序

算法步驟和過程

一趟快速排序的具體做法是:附設兩個指針low和high,它們的初值分別爲low和high,設樞軸記錄的關鍵字爲pivotkey,則首先從high所指位置起向前搜索找到第一個關鍵字小於pivotkey的記錄和樞軸記錄互相交換,然後從low所指位置起向後搜索,找到第一個關鍵字大於pivotkey的記錄和樞軸記錄互相交換,重複這兩步直至low=high爲止。

實質上,快速排序就三個步驟:

第一步:將待排序序列一分爲二

第二步:對小於pivotkey的低子表遞歸排序

第三步:對大於pivotkey的高子表遞歸排序

算法代碼
/**
 * 第一步:將待排序序列一分爲二
 * 第二步: 對低子表遞歸排序
 * 第三步:對高子表遞歸排序
 * @author 卡羅-晨
 */
public class QuickSort {
	
	/**
	 * 將序列一分爲二,並返回樞軸位置
	 */
	private static <T extends Comparable<? super T>> int partition(T[] a,int low,int high) {
		T pivotkey = a[low];
		while(low<high) {
			while(low<high && a[high].compareTo(pivotkey)>=0) --high;
			a[low] = a[high];
			while(low<high && a[low].compareTo(pivotkey)<=0) ++low;
			a[high] = a[low];
		}
		a[low] = pivotkey;
		return low;
	}
	
	/**
	 * 快速排序三步走
	 */
	private static <T extends Comparable<? super T>> void qSort(T[] a,int low,int high) {
		if(low<high) {
			int pivotloc = partition(a, low, high);
			qSort(a, low, pivotloc-1);
			qSort(a, pivotloc+1, high);
		}
	}
	public static <T extends Comparable<? super T>> void quickSort(T[] a) {
		qSort(a, 0, a.length-1);
	}
}

一個方法實現快速排序

public static void quickSort(int[] a,int low,int high) {
		int temp;
		int i = low,j = high;
		if(low < high) {
			temp = a[low];
			while(i != j) {
				while(j>i&&a[j]>=temp) --j;
				if(i<j) {
					a[i] = a[j];
					++i;
				}
				while(i<j&&a[i]<=temp) ++i;
				if(i<j) {
					a[j] = a[i];
					--j;
				}
				a[i] = temp;
				quickSort(a, low, i-1);
				quickSort(a, i+1, high);
			}
		}
	}

3.5、      簡單選擇排序

算法介紹和思想

選擇類排序的主要動作是“選擇”,簡單選擇排序採用最簡單的選擇方式,從頭至尾掃描序列,找出最小的一個記錄,和第一個記錄交換,接着從剩下的記錄中繼續這種選擇和交換嗎,最終使序列有序。

算法代碼

public static void selectSort(int[] a) {
		int minIndex;
		int temp;
		for(int i = 0;i<a.length;i++) {
			minIndex = i;
			for(int j = i+1;j<a.length;j++) {
				if(a[j]<a[minIndex]) {
					minIndex = j;
				}
			}
			if(minIndex != i) {
				temp = a[i];
				a[i] = a[minIndex];
				a[minIndex] = temp;
			}
		}
	}

3.6、      堆排序

算法介紹

是一種數據結構,可以把堆看成一棵完全二叉樹,這棵完全二叉樹滿足:任何一個非葉結點的值都不大於(或不小於)其左右孩子結點的值。若父親大孩子小,叫大頂堆;若父親小孩子大,則這樣的堆叫做小頂堆

二叉樹:每個結點最多隻有兩棵子樹,即二叉樹中結點的度只能爲0、1、2。子樹有左右之分,不能顛倒。

滿二叉樹:在一棵二叉樹中,如果所有的分支結點都有左孩子和右孩子結點,並且葉子結點都集中在二叉樹的最下一層。另一種定義:一棵深度爲k且有 個結點的二叉樹稱爲滿二叉樹。

完全二叉樹:深度爲k,有n個結點的二叉樹,當且僅當其每一個結點都與深度爲k的滿二叉樹中編號從1至n的結點一一對應時,稱之爲完全二叉樹。通俗來講,一棵完全二叉樹是由一棵滿二叉樹從右至左從下而上,挨個刪除結點所得到的。

堆排序的思想:將一個無序序列調整爲一個堆,就可以找出這個序列的最大(或最小)值,然後將找出的這個值交換到序列的最後(或最前),這樣有序序列元素增加·個,無序序列中元素減少1個,對新的無序序列重複這樣的操作,就實現了排序。

堆排序的過程(自述):

1)首先,建堆:原始序列對應一個還不是堆的完全二叉樹。對非葉子節點從右至左,從下至上,進行下濾操作,得到一個大頂堆。

待下濾操作的第一個非葉子節點計算方法:a.length/2-1

下濾操作思想:首先判斷非葉子節點是否爲兩個孩子,兩個孩子誰更大,指針指向大孩子;接着,判斷非葉子節點和指針指向的大孩子誰更大,孩子大則交換。

2)接着,排序:將大頂堆堆頂和無序序列最後一個記錄交換,有序+1記錄,無序-1記錄。

3)然後,將交換後的完全二叉樹,從堆頂下濾,將其調整爲一個大頂堆,重複上述過程。直到樹中只剩下1個節點時排序完成。

堆排序執行過程

l  將原始序列化爲一個完全二叉樹。

l  從無序序列所確定的完全二叉樹的第一個非葉子結點開始,從右至左,從下至上,對每一個結點進行調整,最終將得到一個大頂堆。

對結點的調整方法:將當前結點(假設爲a)的值與其孩子結點進行比較,如果存在大於a值的孩子結點,則從中選出最大的一個與a交換。當a來到下一層的時候重複上述過程,直到a的孩子結點值都小於a的值爲止。

l  將當前無序序列中第一個元素,反映在樹中是根節點(假設爲a)與無序序列中最後一個元素交換(假設爲b)。a進入有序序列,到達最終位置。無序序列中元素減少1個,有序序列中元素增加1個。此時只有結點b可能不滿足堆的定義,對其進行調整。

l  重複3)中的過程,知道無序序列中的元素剩下1個時排序結束

算法代碼(Java)

/**
 * 堆排序
 * @author 卡羅-晨
 */
public class HeapSort {

	public static int leftChild(int i) {
		return 2*i+1;
	}
	
	private static <T extends Comparable<? super T>> void percDown(T[] a,int i,int n) {
		int child;
		T tmp;
		for (tmp = a[i];leftChild(i)<n;i=child) {
			child = leftChild(i);
			if(child != n-1 && a[child].compareTo(a[child+1])<0) {
				child++;
			}
			if(tmp.compareTo(a[child])<0) {
				a[i] = a[child];
			}else 
				break;
		}
		a[i] = tmp;
	}
	/**
	 * 標準堆排序
	 * @param a可比較的集合
	 */
	public static <T extends Comparable<? super T>> void heapsort(T[] a) {
		//建立堆
		for(int i=a.length/2-1;i>=0;i--) {
			percDown(a, i, a.length);
		}
		for(int i=a.length-1;i>0;i--) {
			T tmp = a[0];
			a[0] = a[i];
			a[i] = tmp;
			percDown(a, 0, i);
		}
	}
}

3.7、      二路歸併排序

算法描述

歸併排序(MERGE-SORT是建立在歸併操作上的一種有效的排序算法,該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。

若將兩個有序表合併成一個有序表,稱爲二路歸併

算法思想

1、  劃分:將待排序序列劃分爲兩個長度相等的子序列。

2、  求解子問題:分別對這兩個子序列進行歸併排序,得到兩個有序子序列。

3、  合併:將兩個有序子序列合併成一個有序序列。

算法核心步驟

核心步驟就一下三步:

1、歸併排序前半個子序列

2、歸併排序後半個子序列

3、合併兩個已排序的子序列

其中,前兩個步驟的是一樣的方法mergeSort()進行遞歸,第三步是另一個方法merge()。這個算法中基本的操作就是第三步合併兩個已排序的表。因爲這兩個表是已排序的,所以若將輸出放到第三個表中,則該算法可以通過對輸入數據一趟排序來完成。基本的合併算法是取兩個輸入數組A和B,一個輸出數組C,以及3個計數器Actr、Bctr、Cctr,它們初始置於對應數組的開始端。A[Actr]和B[Bctr]中較小者被拷貝到C中的下一個位置,相關的計數器向前推進一步。當兩個輸入表有一個用完的時候,則將另一個表中剩餘部分拷貝到C中。

實現代碼
public class MergeSortExample {
	public static void mergeSort(int[] a) {
		int[] tempArray = new int[a.length];
		mergeSort(a, tempArray, 0, a.length - 1);
	}
	private static void mergeSort(int[] a, int[] tempArray, int left, int right) {
		if (left < right) {
			int center = (left + right) / 2;
			mergeSort(a, tempArray, left, center);
			mergeSort(a, tempArray, center + 1, right);
			merge(a, tempArray, left, center + 1, right);
		}
	}
	private static void merge(int[] a, int[] tempArray, int leftPos, int rightPos, int rightEnd) {
		int leftEnd = rightPos-1;
		int temPos = leftPos;
		int numElements = rightEnd-leftPos+1;
		while(leftPos<=leftEnd && rightPos<=rightEnd) {
			if(a[leftPos] <= a[rightPos]) {
				tempArray[temPos++] = a[leftPos++];
			}else {
				tempArray[temPos++] = a[rightPos++];
			}
		}
		while(leftPos<=leftEnd) {
			tempArray[temPos++] = a[leftPos++];
		}
		while(rightPos <= rightEnd) {
			tempArray[temPos++] = a[rightPos++];
		}
		for(int i=0;i<numElements;i++,rightEnd--) {
			a[rightEnd] = tempArray[rightEnd];
		}
	}
}

merge()方法很精巧。如果對merge的每個遞歸調用均局部聲明一個臨時數組,那麼在任一時刻就可能有個臨時數組處在活動期。精密的考察表明,由於merge是mergeSort的最後一行,因此在任一時刻只需要一個臨時數組在活動,而且這個臨時數組可以在public型的mergeSort驅動程序中建立。




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