《編程珠璣》的第二部分講的是性能,第三部分講的是應用,所以我暫時跳過第二部分,直接看應用。
第十一章 排序
排序問題一直是面試的熱點!本章首先介紹了插入排序,然後介紹了快速排序,並提出了快速排序的幾種改進方法,例如雙向劃分、隨機數劃分、以及小範圍結合插入排序,三種的性能遞增。
排序免不了交換,書中特別指出將swap()函數寫入循環中會加速。
插入排序: 穩定,時間複雜度O(n^2),主要代碼如下
for ( i = 1 ; i < n ; i ++ )
{
t = x[i];
for( j = i ; j > 0 && x[j-1]>t;j--)
x[j] = x[j-1];
x[j] = t;
}
快速排序: 不穩定;時間複雜度O(nlogn)。
當出現n個相同元素組成的數組時,插入排序性能非常好,總運行時間爲O(n),但qsort性能非常糟糕,因此提出改進。
改進二:採用雙向劃分的方法,代入如下:
void qsort3( l , u )
if l>=u
return
t = x[l]; i = l; j = u + l;
loop
do i++ while i<=u && x[i]<t //從左往右找到第一個大於等於t的數
do j-- while x[j]>t //從右往左找到第一風格小於等於t的數
if i > j
break;
swap( i , j )
swap( l , j)
qsort3( l , j-1 )
qsort3( j+1 , u )
改進三:隨機元素劃分
改進四:快排+插入排序
第十二章 取樣問題
本章主要介紹了生成0~n-1區間內m個隨機數的三種方法,假設bigrand()函數返回一個隨機大整數,例如C庫函數rand()返回15位整數。
方法一:bigrand()%remaining<select,時間複雜度O(n)
方法二:C++zhongSTL的set集合,隨機生成一個數,插入set中,最後有序輸出。用空間換取時間,O(mlogn)
方法三:隨機打亂一個數組,輸出前m個即可,O(n+mlogn)
第十三章 搜索
本章介紹了5中表示隨機整數集合的數據結構:有序數組、有序鏈表、二叉樹、箱、位向量。各自性能如下:
集合表示 |
初始化操作 |
insert操作 |
輸出操作 |
總時間 |
空 間 |
有序數組 |
1 |
m |
m |
O(m^2) |
m |
有序鏈表 |
1 |
m |
m |
O(m^2)
|
2m |
二叉樹 |
1 |
logm |
m |
O(mlogm)
|
3m |
箱 |
m |
1 |
m |
O(m)
|
3m |
位向量 |
n |
1 |
n |
O(n)
|
n/b |
有序數組 IntSetArr類 實現於 P129;
有序鏈表 IntSetList類 實現於 P130;
二叉樹 IntSetBST類 實現於 P132;
位向量 IntSetBitVec類 實現於 P134;
箱 IntSetBins類 實現於 P135;
詳細代碼見《附錄E》
本章的邊欄介紹了“拼寫檢查器”的實現。
第十四章 堆
本章首先介紹了堆的性質:一是有序,二是形狀,以及用數組來表示堆。
堆的兩個關鍵函數是:siftup()自底向上,siftdown()自頂向下,代碼分別實現於P144、P145。
優先級隊列(插入和刪除操作很頻繁)是一種常見的數據結構,主要有三種實現方法:有序序列、無序序列、堆(介於前兩者之間的折中方法),三者性能比較如下:
數據結構 |
一次insert時間 |
一次extract時間 |
兩種操作各n次時間 |
有序序列 |
O(n) |
O(1) |
O(n^2) |
堆 |
O(logn) |
O(logn) |
O(nlogn) |
無序序列 |
O(1) |
O(n) |
O(n^2) |
用堆實現優先級隊列的代碼在P147,建議動手實踐!
最後介紹了堆排序算法,分兩步進行:建堆+排序,O(nlogn),習題2,3對堆排序進行了改進,暫未明白。
堆排序代碼:
for i = [2 , n)
siftup(i)
for( i = n ; i >= 2 ; i-- )
swap( l , i )
siftdown(i-1);
//x[l]是前i個元素中最大的,將它和x[i]交換使得有序序列多一個元素,因此下移保持堆平衡
第十五章 字符串
本章主要介紹了三種字符串的應用:
1、生成詞典,統計單詞的個數,分別用C++和C實現。
C++實現使用了STL的set和map,其實本質是平衡搜索樹。
C實現使用的是散列表,比C++速度更快。
2、最長重複字串,用後綴數組實現,O(nlogn),需要額外的n個指針空間。
3、生成隨機文本,主要了解k階文本-->k接馬爾科夫鏈。