在看這張之前,最好看看我寫的
動態規劃詳解
,裡面都是講理論基礎,我下面的分析都是在此基礎上進展的。
給定乙個由n個互異的關鍵字組成的序列k = (k1, k2, ……, kn),且關鍵字有序(因此有k1 < k2 < …… < kn),從這些關鍵字中構造一棵二叉查詢樹。對每個關鍵字ki, 一次搜尋為ki的概率是pi。某些搜尋的值可能不在k內,因此還有n + 1個「虛擬鍵」d0, d1, d2, ……dn代表不在k內的值,且ki ≤ di ≤ k(i+1),di概率為qi。
因為搜尋每個關鍵字的概率不同,因此最優二叉查詢樹即一棵期望搜尋代價最小的二叉查詢樹。
從下圖a), b)中可以看出,最優二叉查詢樹並不是高度最低的樹,因為第一棵樹期望是2.80,而第二棵是2.75。公式(15.10)顯而易見。
假設一次搜尋的實際代價為檢查的結點個數,亦即,在t內搜尋所發現的結點的深度加上1(這裡深度從0開始算), 所以一次搜尋的期望代價為公式(15.11),期望 = 深度 * 概率。
下面依舊用dp的四個步驟來分析:
①.描述最優解的結構。
基本原則:從n下手,找遞迴。
與小的分組產生遞迴,可否從n個結點中任選一點?可以,因為我們要考察以不同結點為根的情況,所以可以假定任何乙個元素為根節點。由於我們要構造的是二叉查詢樹,左子樹小於根,根小於右子樹,排列有一定的規律。比如我們選擇了k為根節點,那麼p的1→k-1以及q的0→k-1都在k結點的左邊,其餘都在k結點的右邊。我們對1→k-1再進行選取中間節點i遞迴的進行時發現,分成了1→i-1和i+1→k-1,資料的表示範圍左右都在變化。因此設定二維陣列e[i,j]來表示概率和。
②.遞迴定義最優解的值。
我們在i...j中選取任意一點k為根節點,兩邊的分別構成左右子樹,這裡我們強調一下,為了簡便,將e[i,j]設定為一棵完整的樹,其根節點就表示樹根,不是某棵樹的子樹根,而其深度的差異包含在父節點劃分子結構的代價裡面。我們可以寫出e[i,j]=e[i,k-1]+e[k+1,j]+w(i,j)。這裡的w(i,j)如何求?不僅包含了根節點k的概率,還包含了由於i...k-1和k+1...j因為深度加1。當一棵樹成為乙個結點的子樹時,它的搜尋代價的變化:子樹中每個結點的深度增加1,由公式(15.11),這個子樹的期望搜尋代價增加為子樹中所有概率的總和。因此w(i,j)=pi+...pj + qi-1 +...+qj。
考慮邊界條件,由於q為未找到的概率,當i=j時表示查詢到,當j=i-1時表示未找到。所以邊界條件為j=i-1時,e[i,j]=qi-1。
但是這裡必須注意,我們設二維陣列時必須考察陣列的範圍,因為q由0...n,必須能夠滿足讓i>j。把e[i, j]儲存在表e[1……n + 1, 0……n], 第一維的下標需要達到n + 1而不是n, 原因是為了有乙個只包含虛擬鍵dn的子樹,需要計算和儲存e[n + 1, n]。
③.按自底而上的方式計算最優解的值。
再考慮如何自底向上求解。由於r在i...j內變動,其範圍也是有長度決定的,類似於矩陣鏈相乘。所以對e[i,j]遍歷的時候按照長度來算。為了尋求進一步的優化,也可以對w[i,j]進行自底向上。w[i,j]=w[i,j-1]+pj+qj。當j=i-1時,w=qi-1。
④.由計算出的結果創造乙個最優解。
為了能夠重構最優解,儲存分割點r,因此設定同樣的二維陣列root[1...n+1,0...n]
,當遍歷時更新e[i,j]時一同更新root[i,j]=r。
第5, 6行兩層迴圈:
第一次迭代中,l = 1, 迴圈計算e[i, i]和w[i, i],對於i = 1, 2, ……, n。
第二次迭代中,l = 2, 迴圈計算e[i, i + 1]和w[i, i + 1], 對於i = 1, 2, ……, n – 1;
第三次迭代中, l = 3, 迴圈計算e[i, i + 2]和w[i, i + 2], 對於i = 1, 2, ……, n – 2;
如果觀察下邊圖,可以發現,它是自底向上一層一層往上迭代。看似像樹的分布實際上是由表旋轉而來,原來的可以看箭頭所指的圖(旋轉了45°), 9~13行即利用(15.14)的公式跟新root和e,類似文章剛開始的數塔。
**實現:
/*
e[i][j] :
0.05 0.45 0.90 1.25 1.75 2.75
0.10 0.40 0.70 1.20 2.00
0.05 0.25 0.60 1.30
0.05 0.30 0.90
0.05 0.50
0.10
w[i][j]:
0.05 0.30 0.45 0.55 0.70 1.00
0.10 0.25 0.35 0.50 0.80
0.05 0.15 0.30 0.60
0.05 0.20 0.50
0.05 0.35
0.10
root[i][j]:
1 1 2 2 4
2 2 2 4
3 4 5
4 55
*/#include#include#includeusing namespace std;
//以5個元素為例
//k1 < k2 < k3 < k4 < k5
double p[6] = ;
double q[6] = ;
double e[10][10], w[10][10];
int root[10][10];
const int n = 5;
void optimalbst(int n)
for (int l = 1; l <= n; l++)
}//for(r)
}//for(i)
}//for(l) }
int main()
printf("%.2f ", e[i][j]);
}puts("");
}puts("\nw[i][j]:");
for (int i = 1; i <= 6; i++)
printf("%.2f ", w[i][j]);
}puts("");
}puts("\nroot[i][j]:");
for (int i = 1; i <= 5; i++)
printf("%d ", root[i][j]);
}puts("");
}return 0;
}
動態規劃之最優二叉搜尋樹
最優二叉搜尋樹簡介 舉例以及詳細分析 塊測試結果 1 概念引入 基於統計先驗知識,我們可統計出乙個數表 集合 中各元素的查詢概率,理解為集合各元素的出現頻率。比如中文輸入法字型檔中各詞條 單字 片語等 的先驗概率,針對使用者習慣可以自動調整詞頻 所謂動態調頻 高頻先現原則,以減少使用者翻查次數。這就...
動態規劃之最優二叉搜尋樹
最優二叉搜尋樹 public class optimalbst k1.k5的概率 private double q d1.d5的概率 public static void main string args public optimalbst 計算得到最優二叉搜尋樹期望代價 private void ...
動態規劃之最優二叉搜尋樹(演算法導論)
1 一些概念 二叉搜尋樹 在二叉樹中,對任意的節點x其左子樹的所有節點都不大於x.key,其右子樹的所有節點都不小於x.key。滿足此條件的二叉樹稱為二叉搜尋樹。對二叉搜尋樹進行中序遍歷將會得到乙個單調遞增的數列。最優二叉樹 在二叉樹中,不同的節點都有不同的訪問頻率。為了減少查詢某個節點所需要遍歷的...