我的算法學習之路-排序

今天開始看排序算法,漸漸 發現一個事實,腦子有點不夠用……

排序算法主要的性能指標

有三個:

1.時間性能;

2.輔助空間;

3.算法複雜度

(不是時間空間複雜度,就是純粹的代碼複雜程度)。

詳細概念就不抄了……反正總的來說,時間性能一般是大家最看重的,這篇文章裏一共會有7種排序算法。按算法複雜度分爲兩類:

簡單算法:冒泡排序、簡單選擇排序、直接插入排序;

改進算法:希爾算法、堆排序、歸併排序和快速排序。

一個一個來吧~

具體排序算法

1.冒泡排序(Bubble Sort)

冒泡排序,一種交換排序,它的基本思想是:兩兩比較相鄰記錄的關鍵字,如果反序則交換,直到沒有反序的記錄爲止。
直接上代碼:
public class BubbleSort {	
	
	public static void main(String[] args){		
		int[] a = {11,25,32,1,3,4,37,12,33,13,32,10,38,58,7,4,63,33,6,43,4,21,14,24,62,4,42,1};
		Tool.print(a);		
		int[] b = bubbleSort(a);
		Tool.print(b);		
	}
	
	public static int[] bubbleSort(int[] a){
		int length = a.length;
		for(int i=0; i<length; i++){
			for(int j=0;j<length-1-i;j++){
				if(a[j] > a[j+1])
					Tool.swap(a, j, j+1);
			}
		}
		return a;
	}
}	

其中

Tool.print(a);	
…
Tool.swap(a, j, j+1);
是我自己在同一個包下編寫的一個小工具類,這是鏈接

代碼非常簡單,for雙循環,內循環中每兩個相鄰的數都進行比較,如果前邊的數比後邊的數大,兩個數就進行交換。外循環是控制保證每次數組中最大的數都能放到此次循環的最後面。

運行結果:

11 25 32 1 3 4 37 12 33 13 32 10 38 58 7 4 63 33 6 43 4 21 14 24 62 4 42 1 
1 1 3 4 4 4 4 6 7 10 11 12 13 14 21 24 25 32 32 33 33 37 38 42 43 58 62 63 

成功對數組進行了排序,其時間複雜度爲O(n^2)。

2.簡單選擇排序(Simple Selection Sort)

簡單選擇排序就是通過n-i次數組值的比較,從n-i+1個記錄中選出數組值的最小值,並和第i(1<=i<=n)個值交換值。直接上代碼:
public class SimpleSelSort {
	public static void main(String[] args){
		int[] a = {11,25,32,1,3,4,37,12,33,13,32,10,38,58,7,4,63,33,6,43,4,21,14,24,62,4,42,1};
		Tool.print(a);	
		int[] b = simpleSelSort(a);
		Tool.print(b);	
	}
	public static int[] simpleSelSort(int[] a){
		int length = a.length;
		int min;
		for(int i=0; i<length; i++){
			min = i;
			for(int j=i+1; j<length; j++){
				if(a[min]>a[j]){
					min = j;					
				}
			}
			if(min!=i)
				Tool.swap(a, i, min);
		}
		return a;
	}
}

代碼跟冒泡排序類似,先拿出一個值a[i](一般都是數組第一個值a[0]),將它的數組下標保存到一個臨時變量min,此時a[min]指向a[0]。之後開始遍歷數組,如果有數比a[0]小,(例如說a[3])那就把找到的這個數a[3]的下標3賦給臨時變量min,此時a[min]指向a[3],每次遇到比a[min]小的數,都將它的下標賦給min。一遍遍歷結束後,我們得到了數組中最小值的下標(例如是10),之後我們將a[0]和a[10]交換,這時數組的最小值已經存到數組的第一位了。之後再拿出最小值的後面一位(a[1]),然後重複上述步驟,會得到數組中第二小的值並將其存入數組的第二位中。……最後我們會得到一個其中的數從小到大排列的數組。
運行結果:
11 25 32 1 3 4 37 12 33 13 32 10 38 58 7 4 63 33 6 43 4 21 14 24 62 4 42 1 
1 1 3 4 4 4 4 6 7 10 11 12 13 14 21 24 25 32 32 33 33 37 38 42 43 58 62 63 
排序完成,其時間複雜度和冒泡排序一樣,也是O(n^2),但實際上,效率比冒泡算法稍高。

3.直接插入排序(Straight Insertion Sort)

直接插入排序的基本操作是將一個數字插入已經排好序的有序表中,從而得到一個新的、長度加1的有序表。
代碼如下:
public class StraitInsertSort {
	
	public static void main(String[] args){
		int[] a = {11,25,32,1,3,4,37,12,33,13,32,10,38,58,7,4,63,33,6,43,4,21,14,24,62,4,42,1};
		Tool.print(a);			
		int[] b = straitInsertSort(a);
		Tool.print(b);	
	}
	
	public static int[] straitInsertSort(int[] unSorted){
		int length = unSorted.length;		
		for(int i=1; i<length; i++){			
			if(unSorted[i-1]>unSorted[i]){
				int temp;
				int j = i;
				temp = unSorted[i];
				for(; j > 0 && unSorted[j-1] > temp;j --){
					unSorted[j] = unSorted[j-1];	//複製一份給後一位
				}
				unSorted[j] = temp;	//覆蓋後面數值相同兩位中的第一位
			}
		}
		return unSorted;
	}	
}
其代碼邏輯跟上面兩個排序算法稍有不同,但也差距不大。本質上都是雙for循環,這也是它們時間複雜度都是O(n^2)的原因。直接插入算法是在執行內循環之前先進行一個大小判斷,例如如果兩個相鄰的數中前一個數unSorted[3]比後一個數unSorted[4]大(我們想得到的是從小到大的排序)那就將後一個數unSorted[4]存入一個臨時變量temp,之後開始進行循環判斷,如果unSorted[4]前邊的數有大於unSorted[4]的,那就把大的數往後移一位(此時由於unSorted[4]的值已經存入temp中,因爲unSorted[3]比unSorted[4]大,那麼將unSorted[3]的值直接賦給unSorted[4]的位置上,unSorted[4]的值我們仍然可以用。這樣之後unSorted[3]與被覆蓋後的unSorted[4](此時unSorted[4]的值是unSorted[3])都存放了unSorted[3]的值,那麼對unSorted[3]又可以進行判斷,修改的操作了)。直到遇見一個值,它比unSorted[4]小,那麼將unSorted[4]的值存到這個數的後一位(後一位和後後一位存放的是相同的值,不怕被覆蓋)。內循環完成,之後繼續外循環,完成排序。
其結果跟前面一致:
11 25 32 1 3 4 37 12 33 13 32 10 38 58 7 4 63 33 6 43 4 21 14 24 62 4 42 1 
1 1 3 4 4 4 4 6 7 10 11 12 13 14 21 24 25 32 32 33 33 37 38 42 43 58 62 63 

其時間複雜度爲O(n^2),實際中它的性能稍強於冒泡排序和簡單選擇排序。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------簡單排序完成-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

4.希爾排序(Shell Sort)

希爾排序,就是將相聚某個“增量”的記錄組成一個子序列,這樣才能保證在子序列內分別進行直接插入排序後得到的結果是基本有序而不是局部有序。基本有序就是小的關鍵字基本在前面,大的基本在後面,不大不小的基本在中間。概念這種東西,能看懂就看懂了,看不懂也不要緊,重要的是掌握思想。
說白了,希爾算法是插入算法的一種優化,它不是整個數組都去比較,而是將數組先分成相同的好幾塊,每個塊相處於同樣位置的數進行插入排序。
舉個例子,一個年級有300人,要將考試成績進行排序,希爾排序的做法就是先將這300人分成A, B, C 3個班,每班100人,每個班每個人都有1個學號,分別是1~100。先對3個班的1號成績進行比較:按照最高分進C班,其次的進B班,最差的進A班(C>B>A)的順序將3個1號進行排序,之後3個2號,3個3號……直到3個100號進行排序。這樣我們得到的A,B,C3個班,我們可以說分數最低的學生基本在A班(不能直接說都在A班,因爲之前排序的時候可能存在3個人分數都很高,但是必須有人去A班的情況),中等的成績基本在B班,成績較好的基本在C班,這也是之前概念中的意思。
第一次的排序之後,我們將每個班分爲兩個班,A分爲A1和A2,B分爲B1和B2,C分爲C1和C2。每個班都有50人,學號爲1-50。對6個班中學號相同的人繼續進行插入排序,規則也是成績最高的去C2班,之後由高到低去C1,B2,B1,A2,A1。分完之後再將每個班分爲兩個班,然後重複排序動作……直到分的每個班都只有1個人則完成操作(如果是奇數也沒事,就取一多一少即可,最後分到1個人,這些多出來的人還會被排序,並無影響)。好了這就是希爾排序的基本思想,可能會有點抽象。多來幾遍就能理解了,直接上代碼:
public class Test1 {
	public static void main(String[] args){
		int[] a = getRandomArray(9546400);	//生成一個長度爲9546400的數組
		Tool.print("--------------------------------------");
		int[] b = shellSort(a);
		Tool.print(b);
	}
	 public static int[] getRandomArray(int log){
	      int[] result = new int[log];
	      for (int i = 0; i < log; i++) {
	              result[i] = i;
	      }
	      for (int i = 0; i < log; i++) {
	              int random = (int) (log * Math.random());
	              int temp = result[i];
	             result[i] = result[random];
	             result[random] = temp;
	     }
	     return result;
	 }
	 public static int[] shellSort(int[] a) {
		    int d = a.length / 2;		 
		    while (true) {
		        // 把距離爲d的元素編爲一個組,掃描所有組
		        for (int i = d; i < a.length; i++) {
		            int j = 0;
		            int temp = a[i]; 		 
		            // 對距離爲d的元素組進行排序
		            for (j = i - d; j >= 0 && temp < a[j]; j = j - d) {
		                a[j + d] = a[j];
		            }
		            a[j + d] = temp;
		        }		
		        if(d==1) return a;
		        d = d / 2; // 減小增量
		    }		    
		}
}	


代碼的邏輯是在一個while循環裏套兩個for循環,雖然看着套了2個循環,但是由於每次循環都會將工作完成一部分,所以代碼到最後反而效率很高。當d沒有到1的時候會一直執行for循環裏面的for循環,for循環中每個被分好的小組的相同位置的數會進行比較,如果前面的數比後面的大,那麼進行一次交換,之後反覆,直到 i超出數組的範圍,跳出循環,d變成自己之前的一半,繼續進行循環。最後得出正確數組。
題外話:得到的教訓就是要午睡,不然晚上根本沒精神看書,本來準備一天學完另外4個改進排序的,可是隻完成了1個,其餘的只能放到第二天了。。
參考網址:
http://www.cnblogs.com/jingmoxukong/p/4303279.html(參考了代碼和思路,這個講的很清楚)
http://www.cnblogs.com/archimedes/p/shell-sort-algorithm.html(幫助理解不錯)
http://www.cnblogs.com/maxinliang/archive/2012/09/11/2680553.html(隨機數組生成)
http://baike.baidu.com/link?url=I_92ChybM61WzL_1DIYvhepbLInGUiErStKCFG3rEDqe4oDsmjNBsFWYXY1PzZJZCvTnEgiDDYCI2G3SFnmtVJZnTfPay7sAxXoHFCtfz36tjSg5OTfaQ-jfO5XvIRrX(百度百科其中Java版中的簡易版是錯的)

5.堆排序(Heap Sort)

首先我們得知道什麼叫堆,對是具有下列性質的完全二叉樹:每個節點的值都大於或等於其左右孩子節點的值,稱爲大頂堆;或者每個節點的值都小於或者等於其左右孩 子節點的值,稱爲小頂堆。我們排序中用到的是大頂堆,堆從根節點開始,根節點的編號是0,那麼假設i爲某個節點的值,其中i所在節點的孩子編號爲2i+1和2i+2,其父節點編號爲(i-2)/2。大頂堆的性質記爲a[parent(i)] >= a[i]。
堆排序就是利用堆進行排序的方法。它的基本思想是:
(1)將帶排序的序列構成一個大頂堆。此時,整個序列的最大值就是堆頂的根節點;
(2)將它移走(也就是將其與堆數組的末尾 元素交換,此時末尾元素就是最大值),然後將剩餘的n-1個序列重新構造成一個堆,這樣就會得到n個元素中的次小值;
(3)如此反覆執行,便能得到一個有序序列了(此處有些 難理解,建議參考一下之後列出來的參考網址,裏面有圖利於理解)。 
上代碼:
public class HeapSort {
	
	public static void main(String[] args){

		int[] a = Tool.getRandomArray(9546400);	//生成一個長度爲9546400的數組
		Tool.print("--------------------------------------");		
		int[] b = heapSort(a);
		Tool.printL(b);
	}
	
	public static void heapAdjust(int[] array, int parent, int length) {
	    int temp = array[parent]; // temp保存當前父節點	   
	    int child = 2 * parent + 1; // 先獲得左孩子	 
	    while (child < length) {	    		       
	        if (child + 1 < length && array[child] < array[child + 1]) // 如果有右孩子結點,並且右孩子結點的值大於左孩子結點,則選取右孩子結點
	            child++;	           	        
	        if (temp >= array[child])          // 如果父結點的值已經大於孩子結點的值,則直接結束	        
	            break;	 	       
	        array[parent] = array[child];	  // 把孩子結點的值賦給父結點	        
	        parent = child;	        	  // 選取孩子結點的左孩子結點,繼續向下篩選
	        child = 2 * child + 1;	        
	    }	 
	    array[parent] = temp;	    
	}	 
	public static int[] heapSort(int[] list) {
	    // 循環建立初始堆
		int length = list.length;
	    for (int i = length / 2; i >= 0; i--) {
	        heapAdjust(list, i, length - 1);
	    }	    
	    // 進行n-1次循環,完成排序
	    for (int i = length - 1; i > 0; i--) {
	        // 最後一個元素和第一元素進行交換
	        Tool.swap(list, 0, i);	 
	        // 篩選 R[0] 結點,得到i-1個結點的堆
	        heapAdjust(list, 0, i);
	    }	  
	    return list;
	}
}
看代碼可以看到,堆排序分爲兩個函數,其中heapSort()是最後的排序函數,而heapAjust()就是我們之前第一步裏所說的先對堆進行排序,而僅靠它並不能完成使得堆中最大值處於根節點,所以我們可以再heapSort()方法中看到有一個for()循環,它從堆長度的一半開始,逐步往堆頂進行heapAjust()操作,自己稍微畫草圖驗證一下就能知道,這樣做能保證總能從堆的最底層最後一個最後一個節點所在的二叉樹開始進行調整獲取最大值(有點繞),然後逐步往上進行調整,這樣能保證整個二叉樹中的數都能被遍歷,由下往上 而不會被遺漏。最終調整完後,整個數組的最大值就會在在二叉堆的根節點上(此時其它的節點都是無序而需要重新進行排序的,這點很重要)。在heapSort()的第二個for循環中交換根節點和最後一個節點(數組的最後一個值),然後繼續進行調整,數組中的最大值已經被放到數組最後一位,不參加排序,其餘繼續上述過程,最終得到一個從小到大排好序的有序數組。

ps:另外的兩個排序花了兩天……還要想很久,重在理解吧。


參考網址:
http://www.cnblogs.com/jingmoxukong/p/4303826.html(參考了代碼和思路)
http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html(幫助理解)


6.歸併排序(Merging Sort)

“歸併”一詞的中文含義就是合併、併入的意思,而在數據結構中的定義就是將兩個或者兩個以上的有序表組合成一個新的有序表。
歸併排序就是利用歸併的思想實現的排序方法。它的原理是假設初始序列含有n個記錄,澤勒已堪稱是n個有序的子序列,每個子序列的長度爲1,然後兩兩歸併,得到[n/2]+1(不小於n/2的最小整數)個長度爲2或者1的有序子序列,再兩兩歸併,……,如此重複,直到得到一個長度爲n的有序序列爲止,這種排序放大稱爲2路歸併排序。
直接上代碼:
public class MergeSort {
	
	public static void main(String[] args){
//		int[] a = Tool.getRandomArray(9546400);	//生成一個長度爲9546400的數組
		int[] a = {11,25,32,1,3,4,37,12,33,13,32,10,38,58,7,4,63,33,6,43,4,21,14,24,62,4,42,1};
//		Tool.print(a);
		Tool.print("--------------------------------------");
//		int[] b = bubbleSort(a);
		int[] b = mergeSort(a);
		Tool.printL(b);
	}

	public static void merge(int[] a, int low, int mid, int high) {
	    int i = low; // i是第一段序列的下標
	    int j = mid + 1; // j是第二段序列的下標
	    int k = 0; // k是臨時存放合併序列的下標
	    int[] b = new int[high - low + 1]; // array2是臨時合併序列
	    // 掃描第一段和第二段序列,直到有一個掃描結束
	    while (i <= mid && j <= high) {
	        // 判斷第一段和第二段取出的數哪個更小,將其存入合併序列,並繼續向下掃描
	        if (a[i] <= a[j]) {
	            b[k++] = a[i++];
	        } else {
	            b[k++] = a[j++];
	        }
	    }
	    // 若第一段序列還沒掃描完,將其全部複製到合併序列
	    while (i <= mid) {
	        b[k++] = a[i++];	        
	    }
	    // 若第二段序列還沒掃描完,將其全部複製到合併序列
	    while (j <= high) {
	        b[k++] = a[j++];	        
	    }
	    // 將合併序列複製到原始序列中
	    for (k = 0, i = low; i <= high; i++, k++) {
	        a[i] = b[k];
	    }
	}
	public static void mergePass(int[] a, int gap, int length) {
	    int i = 0;
	    //	歸併gap長度的兩個相鄰子表.如果i之後還有兩個gap的長度,就繼續循環否則跳出循環。
	    //	每次進行兩個gap之間的歸併,剛開始gap==1, 每兩個數進行歸併,確定大小後排好序
	    for (i = 0; i + 2 *gap-1 < length; i = i + 2 * gap) {
	        merge(a, i, i + gap - 1, i + 2 * gap - 1);
	    }	    
	    if (i + gap - 1 < length) {// 餘下兩個子表,後者長度小於gap
	        merge(a, i, i + gap - 1, length - 1);
	    }
	}
	public static int[] mergeSort(int[] a) {
	    for (int gap = 1; gap < a.length; gap = 2 * gap) {
	        mergePass(a, gap, a.length);	        
	    }
	    return a;
	}
}
觀察代碼,可以發現代碼分爲三個方法merge(),mergePass(),mergeSort(),這三個方法逐漸上升層次,分別對應了不同的三個功能:
merge():傳入數組a,low,mid和high三個下標。它只對low 和high 之間的數進行基本排序處理(排完序並不能保證絕對有序),mid將這個區間分成兩部分,其中左邊和右邊都有一個下標,分別爲i和j。然後對a[i]和a[j]進行比較,大的一方會存儲到一個新的數組b,並且相對應的下標+1。之後一直循環,直到某一段已經掃描完成,將另一段剩餘的部分直接放入數組b的後面,掃描結束。
mergePass():merge()只是掃描了數組中分好的兩小段,當數組分成多段時,每段長度爲gap,就可以用mergePass()進行整個數組的遍歷掃描,它將merge()運用到了整個數組。其中最後的if()語句是說如果掃描完成,最後剩下的不足兩個小段,那麼直接把最後一段進行當成一個整體進行掃描。最終得出一個有序數組。
mergeSort():讓gap從1開始,逐漸上升到2,4,8,16…,所以數組的有序是從小段到大段的,這樣就保證了數組的整體有序性,最終得到了一個完整的嚴格有序數組。
理解了很久,因爲層層調用,代碼並不是很好理解,但是最終還是理清邏輯了。多思考才能得出最終的結論。

參考網址:
http://www.cnblogs.com/kkun/archive/2011/11/23/merge_sort.html

7.快速排序(Quick Sort)

快速排序的基本思想是:通過一趟排序將待排序記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序的目的。直接上代碼比較直觀:
public class QuickSort {
	public static void main(String[] args){
		int[] a = Tool.getRandomArray(9546400);	//生成一個長度爲9546400的數組
//		int[] a = {11,25,32,1,3,4,37,12,33,13,32,10,
//				38,58,7,4,63,33,6,43,4,21,14,24,62,4,42,1};
//		Tool.printL(a);
		Tool.print("-------------------------------------" +
				"-------------------------------------");
		int[] b = quickSort(a, 0, a.length-1);
		Tool.printL(b);
	}
	public static int[] quickSort(int[] a, int left, int right){		
		if(left<right){
			int pivot = quickAdjust(a, left, right);	
			quickSort(a, left, pivot);
			quickSort(a, pivot+1, right);
		}		
		return a;
	}
	
	public static int quickAdjust(int[] a, int left, int right){	
		int pivot = a[left];		
		while(left < right){
			while(pivot<=a[right] && left < right)
				right--;
			a[left] = a[right];
			while(a[left]<=pivot && left < right)
				left++;
			a[right] = a[left];
		}			
		a[left] = pivot;
		return left;
	}
}
代碼總體是比較明瞭的,簡單說一下吧,這個快速排序中用到了遞歸。先找一個分界點pivot(一般直接選取數組第一個值),分界點的作用是:先將數組通過分界點分爲兩個較小的數組,分界點前所有的數比分界點的數都小,分界點後的數比分界點都大( 其中quickAdjust()方法實現了這一功能),再將每個較小的數組繼續以相同的方法進行分離,排序……最終能得到一個排好序的數組。這樣算初步完成快速排序了,但是有一點,分界點pivot選的太過隨意,萬一它離最值點比較近,那麼快速排序的效率就會變小,所以有改進之處。一般做法是三數取中(median-of-three)法。即去三個掛念自先進行排序,再將中間數作爲分界點,一般是去左端、右端和中間三個數。代碼實現如下:
	public static int quickAdjust(int[] a, int left, int right){	
//		int pivot = a[left];
//		對pivot的取法進行優化
		int pivot;		
		int mid = (left + right) /2;
		if(a[left]>a[right])	//保持右端比左端大
			Tool.swap(a, left, right);
		if(a[mid]>a[right])		//保持右端比中間大
			Tool.swap(a, mid, right);
		//這時右端最大。只需要比較左端和中間,取較大值即可
 		if(a[mid]>a[left]) 
		//保持左端處於中間值
		Tool.swap(a, left, right);
		pivot = a[left];
		while(left < right){
			while(pivot<=a[right] && left < right)
				right--;
			a[left] = a[right];
			while(a[left]<=pivot && left < right)
				left++;
			a[right] = a[left];
		}
			a[left] = pivot;
			return left;
		}







這個目前會報錯……等待處理
參考網址:
http://www.cnblogs.com/jingmoxukong/p/4302891.html

總結

終於寫完了……有點累,一口氣學了這麼多算法,短時間內肯定消化不了,但是隨着時間的推移,會慢慢在學習工作中熟練掌握它們。很重要的一點是掌握排序的思想,改進算法都無一例外地應用了算法中很重要的一個思想——分治法(Divide and Conquer),簡單的說就是將一個大問題劃分成許多小問題,然後分別解決這些小問題,最終達到解決整個大問題。(圖片來源:大話數據結構)

由圖可以看出,其中:
插入排序分爲直接插入排序和希爾排序;
選擇排序分爲簡單選擇排序和堆排序;
交換排序分爲冒泡排序和快速排序;
歸併排序是一個單獨分出來的類。
沒有完美的算法,每個算法都有自身的侷限性,下圖是各個算法的對比:
從算法的簡單性上來看,我們將算法分爲兩類:
簡單算法:冒泡、簡單選擇、直接插入;
改進算法:希爾、堆、歸併、快速。
可以得出的結論是:
(1)平均情況下,改進算法時間性能上明顯優於簡單算法,而希爾算法不如其它三種改進算法;
(2)最壞情況下,堆排序和歸併排序強過快速排序以及其餘簡單排序;
(3)最好情況下,冒泡和直接插入更勝一籌,所以如果帶排序序列基本有序,那就反而應該考慮這兩種算法,而不是四種改進算法;
(4)空間複雜度來說,歸併排序所需要的空間最大,快速排序也有相應空間需求,而堆排序等等對空間要求很低;
(5)穩定性來說,歸併算法一枝獨秀,如果系統非常看重穩定性,那麼就選擇歸併排序;
(6)待排序數n對於算法的選擇也有較大影響,如果n不大,那麼選擇簡單算法是要比改進算法明智的;
(7)綜合各項指標,經過優化的快速排序算法是最好的排序算法,不過在不同的場合也應該考慮使用不同的算法。

參考資料:

大話數據結構

http://www.cnblogs.com/kkun/archive/2011/11/23/2260265.html

http://www.cnblogs.com/jingmoxukong/tag/%E6%8E%92%E5%BA%8F/


算法可視化網站:

這幾個網站能較爲直觀地顯示各個算法的排序過程,能分步觀察,我覺得挺適合初學者的。

https://visualgo-translation.club/zh(可能得用梯子)

http://sorting.at/


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