建議先看看前言:http://www.shnenglu.com/tanky-woo/archive/2011/04/09/143794.html
本章內容頗多,所以我分三篇來寫,這一篇是關于一些基本的概念和選擇,后面兩篇分別是插入和刪除。
上一章總結過BST(http://www.wutianqi.com/?p=2430),BST在高度較小時,可以獲得很好的性能(因為BST的操作的平均時間為O(lgn)),但是在高度較大時,則性能就一般。而紅黑樹“近似平衡”,于是降低了平均時間,再者,紅黑樹并不追求“完全平衡”——它只要求部分地達到平衡要求,降低了對旋轉的要求,從而提高了性能。
談到紅黑樹的用途,最廣為人知的應該就是紅黑樹在C++ STL中的應用了,在set, multiset, map, multimap等中,都應用了紅黑樹(具體大家可以去網上搜搜)。
下面給出紅黑樹和黑高度的定義:
滿足下面幾個條件(紅黑性質)的二叉搜索樹,稱為紅黑樹:
1. 每個結點或是紅色,或是是黑色。
2. 根結點是黑的。
3. 所有的葉結點(NULL)是黑色的。(NULL被視為一個哨兵結點,所有應該指向NULL的指針,都看成指向了NULL結點。)
4. 如果一個結點是紅色的,則它的兩個兒子節點都是黑色的。
5. 對每個結點,從該結點到其子孫結點的所有路徑上包含相同數目的黑結點。
黑高度的定義:
從某個結點出發(不包括該結點)到達一個葉結點的任意一條路徑上,黑色結點的個數成為該結點x的黑高度。
下面就是一個紅黑樹:
紅黑樹是二叉搜索樹的一種。它與普通二叉搜索樹不同的是,紅黑樹的每個節點都附加了另一個屬性――顏色,可以是紅色,也可以是黑色。通過對于每條路徑上節點顏色的規則進行限定,紅黑樹可以保證任何兩條從根部到樹葉的路徑節點個數相差不超過2倍。所以,紅黑樹是一種近似平衡的二叉搜索樹。
紅黑樹的查找、最大值、最小值、前趨、后繼等操作,與普通的二叉搜索樹沒有什么區別。插入和刪除操作需要重新實現。僅僅用普通的二叉搜索樹的插入和刪除動作,可能會破壞紅黑樹本身的一些性質,因此,需要進行額外的處理。這些額外的處理主要是改變樹節點的顏色,或是改變樹的結構。
關于旋轉:
我把13-2手動畫了一次并添加了一些注釋:
旋轉其實比較簡單,就不多說了,以下是代碼:
void LeftRotate(RBTree &T, Node *x)
{
Node *y = x->rchild;
x->rchild = y->lchild;
if(y->lchild != NULL)
y->lchild->parent = x;
y->parent = x->parent;
if(x->parent == NULL)
T = y;
else
{
if(x == x->parent->lchild)
x->parent->lchild = y;
else
x->parent->rchild = y;
}
y->lchild = x;
x->parent = y;
}
void RightRotate(RBTree &T, Node *x)
{
Node *y = x->rchild;
x->rchild = y->lchild;
if(y->lchild != NULL)
y->lchild->parent = x;
y->parent = x->parent;
if(x->parent == NULL)
T = y;
else
{
if(x == x->parent->lchild)
x->parent->lchild = y;
else
x->parent->rchild = y;
}
y->lchild = x;
x->parent = y;
}
下一篇是關于插入。
在我獨立博客上的原文:http://www.wutianqi.com/?p=2438
歡迎大家互相學習,互相討論!
posted @
2011-05-07 09:13 Tanky Woo 閱讀(1679) |
評論 (3) |
編輯 收藏
摘要: 建議先看看前言:http://www.shnenglu.com/tanky-woo/archive/2011/04/09/143794.html
推薦在看算法導論的這一章之前先看看嚴蔚敏老師在《數據結構》上的二叉查找樹。
整體來說二叉查找樹不難,就是插入和刪除節點時讓人糾結,我就是在刪除節點時各種糾結了。
二叉樹執行基本操作的時間與樹的高度成正比。
首先說下二叉查找樹的性質:
設x為二叉查...
閱讀全文
posted @
2011-05-03 12:43 Tanky Woo 閱讀(1872) |
評論 (0) |
編輯 收藏
建議先看看前言:http://www.shnenglu.com/tanky-woo/archive/2011/04/09/143794.html
第10章沒法說,數據結構還是看嚴奶奶的比較好,所以《算法導論》上的這一章我隨便瞄了幾眼就過去了,不過話說回來,數據結構非常重要!!!所以,大家最好把嚴蔚敏的《數據結構》認認真真的看N遍!!!
另外,推薦看看這個:
數據結構的源碼實現:http://www.cppleyuan.com/viewthread.php?tid=418
第11章散列表也屬于數據結構方面的知識,第10章只是講了最基本的幾個結構。這一章也很簡單,其實就是介紹了一些概念及思想,很容易理解。(你可以把散列表想象成平時用的英語字典,26個英文字母就是下標,通過它來定位你要查的單詞。)
所以這一章我就不重復去打出概念了,我把幾個散列函數和處理碰撞的方法放在圖表里方便對比。
①.散列表的優點:出色的期望性能。
②.引出:
直接尋址表(P132)的缺點:
1.全域U也許會很大。
2.實際關鍵字域K也許相對于U會很小。
由此引出了散列表。
以下是我總結對比的表:
這一章我也不知道該怎么說,表面上感覺比較簡單,但是如果深入研究,會發現它的內容太多了,而且很好很強大。所以還是建議大家看看書也許以后深入了解了我會再補充。
這幾天主要在研究紅黑樹在,那個暈啊,不過總算弄明白了,心情也跟著很爽,接下來的一些章節都比較麻煩了,大家一起加油!
在我獨立博客上的原文:http://www.wutianqi.com/?p=2419
歡迎大家互相學習,互相討論!
posted @
2011-04-29 14:14 Tanky Woo 閱讀(1163) |
評論 (0) |
編輯 收藏
建議先看看前言:http://www.shnenglu.com/tanky-woo/archive/2011/04/09/143794.html
這一章的內容很簡單,基本都是一些概念。
第i個順序統計量:在一個由n個元素組成的集合中,第i個順序統計量(order statistic)是該集合中第i小的元素。
最小值是第1個順序統計量(i=1)
最大值是第n個順序統計量(i=n)
中位數:一個中位數(median)是它所在集合的“中點元素”,當n為奇數時,i=(n+1)/2,當n為偶數是,中位數總是出現在
(下中位數)和
(上中位數)。
找最大值/最小值問題,通過比較n-1次可以得出結果。
MINIMUM(A)
1 min ← A[1]
2 for i ← 2 to length[A]
3 do if min > A[i]
4 then min ← A[i]
5 return min
如果要同時找出最大值和最小值,則比較次數最少并不是2*n-2,而是
,我們可以將一對元素比較,然后把較大者于max比較,較小者與min比較,這樣就只需要
。
如果是一般的選擇問題,即找出一段序列第i小的數,看起來要比找最大值或最小值要麻煩,其實兩種問題的漸進時間都是
。
首先看看這個強悍的偽代碼:
RANDOMIZED-SELECT(A, p, r, i)
1 if p = r
2 then return A[p]
3 q ← RANDOMIZED-PARTITION(A, p, r)
4 k ← q - p + 1
5 if i = k ? the pivot value is the answer
6 then return A[q]
7 elseif i < k
8 then return RANDOMIZED-SELECT(A, p, q - 1, i)
9 else return RANDOMIZED-SELECT(A, q + 1, r, i - k)
這個算法利用了隨機化的Partition算法,這個實在第七章的隨機化快排中講到:http://www.wutianqi.com/?p=2368,不記得的可以先復習下前面的快排。
這個隨機化的選擇算法返回數組A[p..r]中第i小的元素。
具體實現如下:
1 /*
2 Author: Tanky Woo
3 Blog: www.WuTianQi.com
4 About: 《算法導論》第9章 查找序列第i小的數字
5 */
6
7 #include <iostream>
8 #include <cstdlib>
9 using namespace std;
10
11 int Partition(int *arr, int beg, int end)
12 {
13 int sentinel = arr[end];
14 int i = beg-1;
15 for(int j=beg; j<=end-1; ++j)
16 {
17 if(arr[j] <= sentinel)
18 {
19 i++;
20 swap(arr[i], arr[j]);
21 }
22 }
23 swap(arr[i+1], arr[end]);
24
25 return i+1;
26 }
27
28 int RandomPartition(int *arr, int beg, int end)
29 {
30 int i = beg + rand() % (end-beg+1);
31 swap(arr[i], arr[end]);
32 return Partition(arr, beg, end);
33 }
34
35
36 int RandomSelect(int *a, int p, int r, int i)
37 {
38 if(p == r)
39 return a[p];
40 int q = Partition(a, p, r);
41 int k = q-p+1;
42 if(i == k)
43 return a[q];
44 else if(i < k)
45 return RandomSelect(a, p, q-1, i);
46 else
47 return RandomSelect(a, q+1, r, i-k);
48 }
49
50 int main()
51 {
52 int a[] = {0, 89, 100, 21, 5, 2, 8, 33, 27, 63};
53 int num = 9;
54 int ith;
55 cout << "序列為: ";
56 for(int i=1; i<=num; ++i)
57 cout << a[i] << " ";
58 cout << endl;
59 ith = RandomSelect(a, 1, num, 2);
60 cout << "序列中第2小的數字是: " << ith << endl;
61 getchar();
62
63 return 0;
64 }
結果如圖:

在(89, 100, 21, 5, 2, 8, 33, 27, 63)中查找第二小的數字是5.
該算法的平均情況性能較好,并且又是隨機化的,所有沒有哪一種特別的輸入會導致最壞情況發生。
在我獨立博客上的原文:
http://www.wutianqi.com/?p=2395歡迎大家互相學習,互相探討。
posted @
2011-04-26 13:00 Tanky Woo 閱讀(1571) |
評論 (1) |
編輯 收藏
摘要: 建議先看看前言 : http://www.shnenglu.com/tanky-woo/archive/2011/04/09/143794.html
這一節講的是非線性排序。
一.計數排序(Counting Sort)
基本思想:對每一個輸入元素x,確定出小于x的元素個數。
適用范圍:適用于輸入是由小范圍的整數構成的序列。
穩定性:算法是穩定的。
具體實現:
1 ...
閱讀全文
posted @
2011-04-24 09:28 Tanky Woo 閱讀(1517) |
評論 (4) |
編輯 收藏
建議先看看前言 : http://www.shnenglu.com/tanky-woo/archive/2011/04/09/143794.html
第八章將介紹幾種非比較排序—計數排序,基數排序,桶排序,這三種排序都在線性時間下運行的。
這一節決策樹其實是對前面的堆排序,快排等是最優的比較算法的證明,
首先說下《算法導論》上對決策樹的定義:一棵決策樹是一棵滿二叉樹(注意看下面解釋),表示某排序算法作用于給定輸入所做的所有比較,而控制結構,移動等都被忽略了。
注意:這里個人認為定義是錯誤的,決策樹不是一棵滿二叉樹,連完全二叉樹都不是。(不知道有沒有朋友看到這里和我想法一樣?)
首先看看只有三個元素時,決策樹的圖:
在決策樹中,每個內結點都用i:j表示比較下標為i數組元素與下標為j的數組元素的大小。每一個葉結點是一個n個元素的全排列。
所以排序算法的執行對應于遍歷一條從樹的根到葉結點的路徑!
因為有n個結點,根據高中學的組合排列知識,知道有n!個情況,也就是n!個葉子結點。
在決策樹中,從根到任意一個可達葉結點之間的最長路徑的長度,表示對應的排序算法中最壞情況下的比較次數。這樣,一個比較算法的最壞情況的比較次數就是其決策樹的高度。
定理8.1證明了任意一個比較算法在最壞情況下都需要做?(n lg n)次的比較。這個證明比較簡單,可以看看書上的證明過程。
這一節其實沒什么內容,就是一點基本的概念,以及了解比較算法可以通過轉換為決策樹這個模型去理解。
posted @
2011-04-21 13:42 Tanky Woo 閱讀(1535) |
評論 (0) |
編輯 收藏
推薦先看看前言:http://www.shnenglu.com/tanky-woo/archive/2011/04/09/143794.html
其實這一篇我老早就寫過了,只不過最近在總結《算法導論》,而第七章就是快速排序,我當初總結的快排也是根據算法導論來的,為了方便大家閱讀,我在這里把曾經寫過的重新再貼一遍。
前幾天寫過一個堆排序的文章(http://www.wutianqi.com/?p=1820),里面謝了很多講解和代碼注釋,個人感覺快速排序不是很難,所以不想寫講解,也不需要寫注釋,大家如果不明白什么是快速排序,可以去看下文章最后我推薦的幾個鏈接。
我查過網上很多關于快排的文章和代碼,但是基本都是最原始的快排,即霍爾(Hoare)快排。想必大家也沒有注意這些,我準備把霍爾快排,算法導論上的快排和隨機化快排的代碼一起發出來,供大家對比與學習,歡迎大家和我探討
代碼一.霍爾快排:
/*
* Author: Tanky Woo
* Blog: www.WuTianQi.com
* Note: 快速排序版本1 --- Hoare-Partition
*/
#include <iostream>
using namespace std;
int num;
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
void PrintArray(int *arr)
{
for(int i=1; i<=num; ++i)
cout << arr[i] << " ";
cout << endl;
}
int Partition1(int *arr, int beg, int end)
{
int low = beg, high = end;
int sentinel = arr[beg];
while(low < high)
{
while(low<high && arr[high]>=sentinel)
--high;
arr[low] = arr[high];
while(low<high && arr[low]<=sentinel)
++low;
arr[high] = arr[low];
}
arr[low] = sentinel;
cout << "排序過程:";
PrintArray(arr);
return low;
}
void QuickSort(int *arr, int beg, int end)
{
if(beg < end)
{
int pivot = Partition1(arr, beg, end);
QuickSort(arr, beg, pivot-1);
QuickSort(arr, pivot+1, end);
}
}
int main()
{
int arr[100];
cout << "Input the num of the elements:\n";
cin >> num;
cout << "Input the elements:\n";
for(int i=1; i<=num; ++i)
cin >> arr[i];
QuickSort(arr, 1, num);
cout << "最后結果:";
PrintArray(arr);
return 0;
}
代碼二.《算法導論》里講的快排:
/*
* Author: Tanky Woo
* Blog: www.WuTianQi.com
* Note: 快速排序版本2---《算法導論》
*/
#include <iostream>
using namespace std;
int num;
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
void PrintArray(int *arr)
{
for(int i=1; i<=num; ++i)
cout << arr[i] << " ";
cout << endl;
}
int Partition2(int *arr, int beg, int end)
{
int sentinel = arr[end];
int i = beg-1;
for(int j=beg; j<=end-1; ++j)
{
if(arr[j] <= sentinel)
{
i++;
swap(arr[i], arr[j]);
}
}
swap(arr[i+1], arr[end]);
cout << "排序過程:";
PrintArray(arr);
return i+1;
}
void QuickSort(int *arr, int beg, int end)
{
if(beg < end)
{
int pivot = Partition2(arr, beg, end);
QuickSort(arr, beg, pivot-1);
QuickSort(arr, pivot+1, end);
}
}
int main()
{
int arr[100];
cout << "Input the num of the elements:\n";
cin >> num;
cout << "Input the elements:\n";
for(int i=1; i<=num; ++i)
cin >> arr[i];
QuickSort(arr, 1, num);
cout << "最后結果:";
PrintArray(arr);
return 0;
}
代碼三.隨機快排:
/*
* Author: Tanky Woo
* Blog: www.WuTianQi.com
* Note: 快速排序版本3 --- 隨機化版本
* 解決待排序元素相差很大的情況
*/
#include <iostream>
#include <cstdlib>
using namespace std;
int num;
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
void PrintArray(int *arr)
{
for(int i=1; i<=num; ++i)
cout << arr[i] << " ";
cout << endl;
}
int Partition3(int *arr, int beg, int end)
{
int sentinel = arr[end];
int i = beg-1;
for(int j=beg; j<=end-1; ++j)
{
if(arr[j] <= sentinel)
{
i++;
swap(arr[i], arr[j]);
}
}
swap(arr[i+1], arr[end]);
cout << "排序過程:";
PrintArray(arr);
return i+1;
}
int RandomPartition(int *arr, int beg, int end)
{
int i = beg + rand() % (end-beg+1);
swap(arr[i], arr[end]);
return Partition3(arr, beg, end);
}
void RandomQuickSort(int *arr, int beg, int end)
{
if(beg < end)
{
int pivot = RandomPartition(arr, beg, end);
RandomQuickSort(arr, beg, pivot-1);
RandomQuickSort(arr, pivot+1, end);
}
}
int main()
{
int arr[100];
cout << "Input the num of the elements:\n";
cin >> num;
cout << "Input the elements:\n";
for(int i=1; i<=num; ++i)
cin >> arr[i];
RandomQuickSort(arr, 1, num);
cout << "最后結果:";
PrintArray(arr);
return 0;
}
最后,我想說下,隨機化的快排一般適用于待排序的數據之間相差較大的情況下。
這里給出幾篇網上講得不錯的文章:
1.http://bbs.chinaunix.net/viewthread.php?tid=1011316
算是一個討論帖。很給力!
2.http://www.javaeye.com/topic/561718
講得比較詳細,不過代碼是Java的。
3.http://tayoto.blog.hexun.com/25048556_d.html
4.http://www.360doc.com/content/10/1106/11/1317564_67067368.shtml
概念上很詳細。
5.http://blog.csdn.net/wssxy/archive/2008/12/05/3448642.aspx
一篇講快排的佳作!
6.http://www.cnblogs.com/chinazhangjie/archive/2010/12/09/1901491.html
小杰的文章,用C++模板類寫的。大家可以去學習學習。
在我獨立博客上的原文:http://www.wutianqi.com/?p=2368
歡迎大家互相學習,互相探討。
posted @
2011-04-19 18:08 Tanky Woo 閱讀(2026) |
評論 (7) |
編輯 收藏
建議先看看前言:http://www.shnenglu.com/tanky-woo/archive/2011/04/09/143794.html
上一章總結是的堆排序算法,這一章同樣是利用了堆這種數據結構,實現在是優先級隊列。
根據堆分為最大堆,最小堆,所以優先級隊列也可以分為最大優先級隊列和最小優先級隊列。
優先級隊列的概念和用途書上已經寫的很清楚了,我就不再打一遍了。直接寫出具體實現。
在實現前先說幾點:
1.上一章說過,堆的length和heapsize要區分清楚,這一章的優先級隊列里就用到了。
2.優先級隊列用到了上一章的一些函數比如MaxHeapify(),不記得的可以復習下上一章。
以下是代碼及講解(此處實現的是最大優先級隊列):
// 堆應用之優先級隊列
// Tanky Woo
// Blog: www.WuTianQi.com
#include <iostream>
using namespace std;
const int INF = 999999;
/////////////////////////////////////////////////////////
////////////// 以下代碼在堆排序中已講解過 ///////////////
void MaxHeapify(int *a, int i, int len)
{
int lt = 2*i, rt = 2*i+1;
int largest;
if(lt <= len && a[lt] > a[i])
largest = lt;
else
largest = i;
if(rt <= len && a[rt] > a[largest])
largest = rt;
if(largest != i)
{
int temp = a[i];
a[i] = a[largest];
a[largest] = temp;
MaxHeapify(a, largest, len);
}
}
void BuildMaxHeap(int *a, int size)
{
for(int i=size/2; i>=1; --i)
MaxHeapify(a, i, size);
}
void PrintArray(int data[], int size)
{
for (int i=1; i<=size; ++i)
cout <<data[i]<<" ";
cout<< endl << endl;
}
////////////////////////////////////////////////////////////
// 返回具有最大關鍵字的元素
int HeapMaximum(int *a)
{
return a[1];
}
// 去掉并返回具有最大關鍵字的元素
// 注意:這里每次MaxHeapify的是heapsize
int HeapExtractMax(int *a, int &heapsize)
{
if(heapsize < 1)
cout << "Heap Underflow" << endl;
int max = a[1];
a[1] = a[heapsize];
--heapsize;
MaxHeapify(a, 1, heapsize);
return max;
}
// 將元素a[i]的值增加到key
void HeapIncreaseKey(int *a, int i, int key)
{
if(key < a[i])
cout << "New key is smaller than current key" << endl;
a[i] = key;
while(i > 1 &&a[i/2] < a[i])
{
int temp = a[i];
a[i] = a[i/2];
a[i/2] = temp;
i /= 2;
}
}
// 插入關鍵字為key的元素
void MaxHeapInsert(int *a, int key, int &heapsize)
{
++heapsize;
a[heapsize] = -INF;
HeapIncreaseKey(a, heapsize, key);
}
int main()
{
int len, heapsize;
int arr[100] = {0, 15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 2, 1};
// 區別len 和 heapsize
// heapsize是堆的大小,而len是初始數組的總大小。
len = heapsize = 12;
// 首先建堆
BuildMaxHeap(arr, len);
cout << "建堆后: " << endl;
PrintArray(arr, len);
// 使用HeapMaximum
cout << "當前最大的元素是: " << endl;
cout << HeapMaximum(arr) << endl << endl;
// 使用HeapExtractMax
cout << "使用HeapExtractMax后: " << endl;
HeapExtractMax(arr,heapsize);
PrintArray(arr, heapsize);
// 再次使用HeapExtractMax
cout << "再次使用HeapExtractMax后: " << endl;
HeapExtractMax(arr,heapsize);
PrintArray(arr, heapsize);
// 使用HeapIncreaseKey
cout << "使用HeapIncreaseKey后: " << endl;
HeapIncreaseKey(arr, 2, 15);
PrintArray(arr, heapsize);
// 使用MaxHeapInsert
cout << "使用MaxHeapInsert后: " << endl;
MaxHeapInsert(arr, 28, heapsize);
PrintArray(arr, heapsize);
}
以下是運行結果:

看上圖的結果:
在第二次使用HeapExtractMax后,把第二個數字即6設為15,更新后,結果就是HeapIncreaseKey的輸出。
posted @
2011-04-17 15:00 Tanky Woo 閱讀(1519) |
評論 (0) |
編輯 收藏
建議先看看前言:http://www.shnenglu.com/tanky-woo/archive/2011/04/09/143794.html
首先介紹幾個概念:
衛星數據:一個帶排序的的數通常是有一個稱為記錄的數據集組成的,每一個記錄有一個關鍵字key,記錄的其他數據稱為衛星數據。
原地排序:在排序輸入數組時,只有常數個元素被存放到數組以外的空間中去。
在第二章介紹了兩種排序:插入排序和合并排序,接下來兩章要介紹的是推排序和快速排序,這四個排序都屬于比較排序(comparison sort)。
我以前總結過堆排序,并具體實現了堆排序,代碼中給出了詳細的注釋,所以在這里就不重復發了,大家可以去看看,個人覺得總結的還是比較給力的:
這里再補充幾點:
1.區別length[A]和heap-sort[A]。(P73)(這個在下一篇的優先級隊列中將會具體區別)
2.總體上看堆排序由三個函數組成:①.MAX-HEAPIFY ②.BUILD-MAX-HEAP ③.HEAP-SORT
另外,在這里給大家補充一點個人經驗,有時理論難以理解,代碼難以理解,這個時候,就要靠秘訣了:拿起手中的筆和紙,自己給出一組輸入,按照書上的代碼,自己去模擬這組輸入的執行過程。(這個過程人人都知道,但并不是人人都去做了!學算法,就要自己去模擬,去畫圖,去推!怎么樣容易理解就怎么去做!)
所以這也是我喜歡《算法導論》的原因,接下來,就要強烈推薦大家看《算法導論》上非常非常給力的堆排序實現圖了—圖6-4。
總結:本章最基礎也是最重要的就是理解堆這種結構!
堆是什么?來看看《算法導論》上的圖6-1:
圖(a)是一個最大堆,圖(b)是最大堆的數組表示。可以看到堆的數組并不是已排序好的。
讓我們來回憶下最大堆的定義(P74):
在最大堆中,最大堆特性是指除了根以外的每個結點i,有A[PARENT(i)] >= A[i]。這樣,堆的最大元素就存放在根結點中。
對,堆排序就是利用的這個特性—“堆的最大元素就存放在根結點中”
每次堆化,這樣就找到了當前堆的最大元素。
所以說,理解了其本質特征,堆排序其實很簡單的。
至于堆排序的具體應用,在后面的最短路算法—Dijkstra中,會用到由堆來優化普通的Dijkstra算法。
下一篇將實現最大優先級隊列。
posted @
2011-04-15 12:43 Tanky Woo 閱讀(1314) |
評論 (0) |
編輯 收藏
建議先看看前言:http://www.shnenglu.com/tanky-woo/archive/2011/04/10/143850.html#143880
因為《算法導論》第一部分1~5章的理論性太強,研究過多容易糾結,所以索性合起來快點講過去。
第四章:
這一章講的是遞歸式(recurrence),遞歸式是一組等式或不等式,它所描述的函數是用在更小的輸入下該函數的值來定義的。
本章講了三種方法來解遞歸式,分別是代換法,遞歸樹方法,主方法。
1.代換法(Substitution method)(P38~P40)
定義:即在歸納假設時,用所猜測的值去代替函數的解。
用途:確定一個遞歸式的上界或下界。
缺點:只能用于解的形式很容易猜的情形。
總結:這種方法需要經驗的積累,可以通過轉換為先前見過的類似遞歸式來求解。
2.遞歸樹方法(Recursion-tree method)
起因:代換法有時很難得到一個正確的好的猜測值。
用途:畫出一個遞歸樹是一種得到好猜測的直接方法。
分析(重點):在遞歸樹中,每一個結點都代表遞歸函數調用集合中一個子問題的代價。將遞歸樹中每一層內的代價相加得到一個每層代價的集合,再將每層的代價相加得到遞歸式所有層次的總代價。
總結:遞歸樹最適合用來產生好的猜測,然后用代換法加以驗證。
遞歸樹擴展過程:①.第二章2.3.2節分析分治法時圖2-5(P21~P22)的構造遞歸樹過程;②.第四章P41圖4-1的遞歸樹構造過程;這兩個圖需要好好分析。
3.主方法(Master method)
優點:針對形如T(n) = aT(n/b) + f(n)的遞歸式
缺點:并不能解所有形如上式的遞歸式的解。
具體分析:
T(n) = aT(n/b) + f(n)描述了將規模為n的問題劃分為a個子問題的算法的運行時間,每個子問題的規模為n/b。
在這里可以看到,分治法就相當于a=2, b=2, f(n) = O(n).
主方法依賴于主定理:(圖片點擊放大)
圖片可以不清晰,可以看書。
主定理的三種情況,經過分析,可以發現都是把f(n)與
比較。
第一種情況是
更大,第二種情況是
與f(n)相等,第三種情況是f(n)更大。
但是,這三種情況并未完全覆蓋所有可能的f(n):
第一種情況是f(n)多項式的小于
,而第三種情況是f(n)多項式的大于
,即兩者相差的是
。如果兩者相差的不是
,則無法用主定理來確定界。
比如算法導論P44最下面的
就不能用主定理來判斷。
至于主定理的證明,有興趣的可以拿筆在紙上推算,反正我這里是沒看的,畢竟時間有限,要選擇性的學習。
第五章:
本章是圍繞一個雇傭問題展開的。
問題:有一批參與面試的人,你要一個個面試(面試每個人都要花費c1),如果當前面試者比自己的助理能力強,則辭掉當前助理的,并把當前面試者提拔為助理(雇傭一個人要花費c2),一直面試完所有人。
這里考慮的是面試所花的money,假設總共有N人參加面試,有M人被雇傭過,則花費O(N*c1 + M*c2),因為參與面試的人員順序是不確定的,所以要花費的money也是不確定的(N*c1是確定的,而M是不確定的,所以M*c2也是不確定的)。
首先介紹兩個概念:
1.概率分析:指在問題的分析中應用概率的技術。
2.隨機算法:如果一個算法的行為不只是由輸入決定,同時也由隨機數生成器所產生的數值決定,則成這個算法是隨機的。
書上講的理論性有點強,我這里用自己的話來說下:
首先說下概率分析,因為前面講到過:雇傭所需的花費與輸入序列有關,有N個面試人員(考慮每個人的能力不一樣),則一共有N!中排列情況(即每種排列出現的概率是1/(N!)),于是假設每種排列花費Ti元,則所有供花費:
T1/(N!) + T2/(N!) + … + TN/(N!)。
其實這里可以結合高中學的正態分布來理解,都是講的每種情況出現的概率,思想差不多,所以這里也不需要什么概率論的知識,都是一些常識。
順便補充一下:5.2節的指示器隨機變量就是用的高中學的期望,用期望來表示概率。
再說下隨機算法,對于上面概率分析時的方法,雖然面試人員的排列是不確定的。但是如果當排列確定后,則所需花費也就確定了。而對于隨機算法,就算排列確定,其花費也是不確定的。即隨機發生在算法上,而不是發生在輸入分布上。這就是概率分析與隨機算法之間的區別。
比如按書上的,可以給每個人員隨機生成一個優先級,或者把每一個面試人員與其后的面試人員中隨機一員交換,來產生一個隨機的序列。
我以前總結過一些隨機算法,有興趣的朋友可以去看看:
1.《隨機化算法(1) — 隨機數》
2.《隨機化算法(2) — 數值概率算法》
3.《隨機化算法(3) — 舍伍德(Sherwood)算法》
4.《隨機化算法(4) — 拉斯維加斯(Las Vegas)算法》
5.《隨機化算法(5) — 蒙特卡羅(Monte Carlo)算法》
另外,比如像C/C++中庫函數rand()就是一個產生隨機變量的函數,但是它并不是真正意義上的隨機函數,所以稱之為偽隨機函數。因為當srand(seed)設置的seed一樣時,則rand()產生的隨機數也一樣,所以通常可以通過rand(-1)來把當前時間作為種子模擬隨機函數。
補充:在第五章的5.4節給出了幾個題目及其分析,這幾個題目都很有趣,不過對于數學也相對有一定的要求。其實可以很簡化的想:概率和期望是互相轉化的,這幾題就可以考慮是去求期望的。
我昨天在論壇出了一個邏輯面試題:一樓到十樓的每層電梯門口都放著一顆鉆石,鉆石大小不一。你乘坐電梯 從一樓到十樓,每層樓電梯門都會打開一次,只能拿一次鉆石,問怎樣才能拿到最大的一顆?
想必好多人都看過這題,網上的解答多種多項,我覺得此題應該考察的是最優解問題,按照最優解的思路,此題沒有100%的解決方法,只能盡量使其期望更高,也就是概率更大。這一題可以說是數學和哲學的完美結合,有點像人生,總想得到更多,但又怕后面的都不行,各種糾結啊。。。
總的來說,第五章說來說去都是一個期望的問題。
posted @
2011-04-12 12:40 Tanky Woo 閱讀(2348) |
評論 (0) |
編輯 收藏