就算複習再緊張,也要動手實踐資料結構中的基本演算法,徹底理解演算法的本質。不要讓任何理由成為不編程的借口。從快速排序開始,將演算法問題一一攻克。複習提綱:
基本資料結構:
棧和隊列,數組和鏈表
樹和二叉樹
二叉尋找樹
平衡二叉樹
散列表
排序演算法:
選擇排序、冒泡排序和快速排序
堆和堆排序
計數排序、基數排序和桶排序
搜尋演算法、貪心演算法和動態規劃:
深度優先搜尋和廣度優先搜尋
搜尋時的剪枝
貪心演算法和赫夫曼編碼
動態規劃
圖論:
拓撲排序
強聯通分量
最小產生樹
單源最短路
二分圖匹配
網路流
其它演算法:
數學演算法
字串匹配和自動機
計算幾何學(凸包和最近/最遠點對)
尾碼樹和尾碼數組
參考書籍:
《資料結構與演算法分析-C++描述》
《C++資料結構原理與經典問題求解》
一.快速排序
演算法描述:
quicksort是在目前最快排序演算法,它的平均已耗用時間是O(NlogN),最壞情形的效能為O(N^2),空間複雜度為O(N)。它採取了一種分治策略,其基本思想是取待排序對象序列中的某個對象為基準,按照其值將對象序列劃分為左右兩個子序列,左側的元素都小於或等於基準值,右側的元素都大於或等於基準值。然後分別對左右子列重複上述操作,直到排序完畢。
將數組S排序的基本演算法由以下4步組成:
(1).如果S中元素個數是0或1,則返回。
(2).取S中任一元素v,稱之為樞紐元(pivot)。
(3).將S-{v}(S中其餘元素)劃分成為兩個不相交的交集:S1 = {x∈S-{v} | x <= v}和 S2 = {x∈S-{v} | x >= v}。
(4).返回{quicksort(S1),v,qucksort(S2)}。
過程如下:
13 81 33 24 45 9 78 0 (排序之前)
13 81 33 2445 9 78 0 (選取pivot 24)
[13 9 0]24 [33 45 78 81] (劃分左右子列)
重複(2)(3)(4),對左右子列進行遞迴調用快速排序,直到(1)成立,排序完成。
快速排序的實現:
//快速排序:劣質版#include <iostream>int partition2(int data[],int l,int h){int i=l;int j=h;int privot = data[l];//每次都選取第一個元素作為樞紐元while (i<j)//關鍵步驟:把左邊的一個較大的數和右邊的一個較小的數對換{while(data[j]>=privot && i<j)//每次把右邊比樞紐元小的換到左邊j--;data[i] = data[j];while(data[i]<=privot && i<j)//把左邊的較大元素換到右邊,和剛才的data[j]位置對換i++;data[j] = data[i];}data[i] = privot;//把臨界位置的值換成樞紐元return i;}void quicksort(int data[],int l,int h){if(l<h){int k = partition2(data,l,h);quicksort(data,1,k-1);quicksort(data,k+1,h);}}void testQuickSort(){int data[] = {2,3,23,545,6,7,32,75,71,-45,-123,0,89};quicksort(data,0,12);for(int i=0;i<13;++i)std::cout<<data[i]<<" ";std::cout<<std::endl;}
上面演算法雖實現了快速排序,但只是簡單的選取第一個元素作為樞紐元。如果是隨機的輸入,這樣做影響不大,但如果輸入的是預排序或者反序的,那麼這樣選取的樞紐元就會產生一個劣質的分割,所有的元素不是劃入S1就是劃入S2。就有可能每次遞迴的時候
演算法分析:
快速排序的時間為左右子列的排序時間加上分割所花的線性時間(樞紐元的選取耗時為常數,這裡不考慮):
T(N) = T(i) + T(N-i-1) +cN
最壞情況分析: