最優二叉查詢樹—動態規劃C++
問題描述
1、問題描述:
基於統計先驗知識,我們可統計出一個數表(集合)中各元素的查詢概率,理解為集合各元素的出現頻率。比如中文輸入法字型檔中各詞條(單字、片語等)的先驗概率,針對使用者習慣可以自動調整詞頻——所謂動態調頻、高頻先現原則,以減少使用者翻查次數。這就是最優二叉查詢樹問題:查詢過程中鍵值比較次數最少,或者說希望用最少的鍵值比較次數找到每個關鍵碼(鍵值)。為解決這樣的問題,顯然需要對集合的每個元素賦予一個特殊屬性——查詢概率。這樣我們就需要構造一顆最優二叉查詢樹。
n個鍵{a1,a2,a3…an},其相應的查詢概率為{p1,p2,p3…pn}。構成最優BST,表示為T1n ,求這棵樹的平均查詢次數C[1, n](耗費最低)。換言之,如何構造這棵最優BST,使得C[1, n] 最小。
動態規劃演算法解題思路
動態規劃法策略是將問題分成多個階段,逐段推進計算,後繼例項解由其直接前趨例項解計算得到。對於最優BST問題,利用減一技術和最優性原則,如果前n-1個節點構成最優BST,加入一個節點 an 後要求構成規模n的最優BST。按 n-1, n-2 , … , 2, 1 遞迴,問題可解。自底向上計算:C[1, 2]→C[1, 3] →… →C[1, n]。為不失一般性用C[i, j] 表示由{a1,a2,a3…an}構成的BST的耗費。其中1≤i ≤j ≤n。這棵樹表示為Tij。從中選擇一個鍵ak作根節點,它的左子樹為Tik-1,右子樹為Tk+1j。要求選擇的k 使得整棵樹的平均查詢次數C[i, j]最小。左右子樹遞迴執行此過程。(根的生成過程)
引用書上求解遞迴式:
以輸入節點概率 :0.10 0.15 0.20 0.25 0.30為例
在計算C[ i ] [ j ]是需要從上往下按對角線計算,:
下面我們來計算C [ 1 ] [ 2 ] :
1<=i<=j<=2;
I<=k<=j;
解題思路圖形化
下面介紹一種更為直觀的三角形計算方法,其實本質沒有發生改變,只是把數學公式圖形化,方便填表。
求C[i][j]時,設以C[i][i-1],C[j+1][j],C[i][j]三個點形成的直角三角形的次斜邊上的值累加和為S(斜),直角邊上對應兩點的和為S(直),顯然S[直]有j-i+1個,則C[i][j]=S[斜]+min{S[直]}。更方便的理解為將最優樹劃分為左右子樹,尋求其最小和值(構建最優二叉樹),同時也能由k值確認最優的根節點值。
下面舉個例子求C[1][3]的值,如圖所示:
S(斜)=0.1+0.2+0.4=0.7,S(直)=min{S1(直),S2(直),S3(直)}=min{0+0.8,0.1+0.4,0.4+0}=0.4,則C[1][3]=0.7+0.4=1.1。同理,求C[[2]][[4]],如下圖所示,C[[2]][[4]]=1.4:
思考:為什麼輸入概率相同(無序),輸出結果不一樣
總結一句話就是:輸入資料是有序的,12345分別代表其根值大小,即第一個輸入概率對應的值是1,第二個是2,查詢的時候並不是根據概率查詢,而是根據根值來構造最優二叉查詢樹。下面詳細分析:
- 輸出樹不一致
遇到的問題是輸入同樣的資料,就是各個概率順序不一樣,為什麼得到的最優二叉查詢樹不一樣。
例如:每個節點對應的查詢概率值:0.10 0.15 0.20 0.25 0.30
那麼最優二叉樹畫出來就是:
但是當輸入概率是:0.25 0.10 0.15 0.20 0.30
那麼最優二叉樹畫出來就是:
從這兩個圖可以看出來,它們的最優平均查詢次數不一樣,連樹畫出來也不一樣。
剛開始我是考慮怎樣最優二叉樹的查詢,如果按照我剛開始的理解就是按照概率查詢,其實是錯的。那麼按照概率查詢,第二種二叉樹的畫法就出錯了,因為從根節點0.15 看,它的左右子節點都比它大,下一步就不知道該查詢那邊的節點了。
那麼我想了一種解決方法,就是像圖一的二叉樹一樣,將二叉樹的概率進行升序排序,那麼它的查詢樹就會符合最優二叉樹的查詢方法,它的左邊節點全部小於根節點,右邊的左根節點都大於根節點。如果一定要排序的話那麼就說明並不具有通用性,這個動態規劃的查詢最優二叉樹的演算法就出錯了。
那麼我就開始考慮是否是我錯了,演算法算出的每一種結果都是對的,然後我就將所有概率換成了根節點。經過多次的結果驗證,發現每個圖都符合最優二叉樹查詢的規律,經過仔細的思考我終於理解了:因為查詢的鍵值是根據根節點,而不是根據概率,而且每個鍵值都是升序。就像跟幾點是1,2 ,3 ,4 ,5,那麼它的鍵值大小排序就是1<2<3<4<5,這也就解釋了求概率矩陣的時候為什麼c[ I ] [ j ]是i到j時連續的,而不是跳變的。所以這個問題隱藏了一個條件,就是輸入的概率對應的鍵值是升序,那麼第二個最優二叉樹應該是:
流程
詳細流程圖
例項
輸入輸入概率:0.25 0.10 0.15 0.20 0.30
對應最優二叉查詢樹:
程式碼
C++寫的程式碼:
//最優二叉查詢樹
#include<bits/stdc++.h>
using namespace std;
//const int maxval = 9999;
double BST(int n,double p[],double c[][100],int r[][100])
{
for(int i=1;i<=n;i++)
{
c[i][i-1]=0; //Ci矩陣初始化
c[i][i]=p[i];
r[i][i]=i; //R根矩陣初始化
}
c[n+1][n]=0;
for(int d=1;d<n;d++) //安對角線計算,從第二條對角線開始
{
for(int i=1;i<=n-d;i++) //行的取值範圍
{
int j=i+d; //j求出在對角線上的i對應的
double minval=9999;
int mink=i; //最小值對應根點
double sum=0;
for(int k=i;k<=j;k++)
{
sum=sum+p[k];
if(c[i][k-1]+c[k+1][j]<minval) //三角形比較法,選最小值
{
minval=c[i][k-1]+c[k+1][j];
mink=k;
}
}
c[i][j]=minval+sum;//得到了最小值
r[i][j]=mink;//記錄取得最小值時的根節點
}
}
return c[1][n];
}
int main()
{
while(1)
{
cout<<"input operand : 1 enter ,2 exit system"<<endl;
int ch;
cin>>ch;
if(ch==2)
return 0;
else if(ch==1)
{
system("cls");
int n;
cout << "input the point number" << endl;
cin>>n; //節點個數
double p[n]; // 概率陣列
memset(p,0,sizeof(p));
// 將概率陣列排序,保證正確
cout<<"input each point probability" <<endl;
for(int i=1;i<=n;i++)
{
cin>>p[i];
}
double c[n+2][100]; //動態ci矩陣
int r[n+2][100]; //根矩陣
memset(r,0,sizeof(r));
memset(c,0,sizeof(c));
double s=BST(n,p,c,r);
cout << "the minimum compare times is " << s<<endl;
cout << "the minimum probability matrix is" << endl;
for(int i=1;i<=n+1;i++)
{
for(int j=0;j<=n;j++)
{
cout<<std::setw(3);
cout << c[i][j] << " ";
}
cout << endl;
}
cout << "the point matrix " << endl;
for(int i=1;i<=n+1;i++)
{
for(int j=0;j<=n;j++)
{
cout << r[i][j] << " ";
}
cout << endl;
}
}
}
}
相關文章
- 滿二叉樹、完全二叉樹、平衡二叉樹、二叉搜尋樹(二叉查詢樹)和最優二叉樹二叉樹
- 動態規劃---求硬幣最優解動態規劃
- (C++)DP動態規劃C++動態規劃
- <動態規劃>Leetcode96.不同的二叉搜尋樹動態規劃LeetCode
- 二叉查詢樹
- c++ 動態規劃(數塔)C++動態規劃
- 手擼二叉樹——二叉查詢樹二叉樹
- 斜率優化動態規劃優化動態規劃
- PHP使用動態規劃實現最優紅包組合PHP動態規劃
- 二叉查詢樹【二叉排序樹】構建和查詢演算法 PHP 版排序演算法PHP
- BZOJ 3589 動態樹(子樹操作,鏈查詢)
- 二叉查詢樹的插入刪除查詢
- 平衡二叉查詢樹:紅黑樹
- 動態規劃:最長上升子序列動態規劃
- 最長上升子序列動態規劃動態規劃
- 動態規劃求最長降序序列動態規劃
- 動態規劃-最長公共子序列動態規劃
- 動態規劃——最長公共子序列動態規劃
- 二叉樹路徑查詢二叉樹
- Amazing tree —— 二叉查詢樹
- 【動態規劃】樹形DP完全詳解!動態規劃
- 二叉查詢樹和笛卡爾樹
- 霍夫曼樹(最優二叉樹)的實現二叉樹
- 【LeetCode動態規劃#04】不同的二叉搜尋樹(找規律,有點像智力題)LeetCode動態規劃
- 動態規劃(最長公共子序列LCS)動態規劃
- 動態規劃-最長上升子序列模型動態規劃模型
- 二叉查詢樹(查詢、插入、刪除)——C語言C語言
- c++踩方格-動態規劃基礎題C++動態規劃
- 834. 樹中距離之和-困難-樹、圖、動態規劃、深度優先搜尋動態規劃
- 5分鐘瞭解二叉樹之二叉查詢樹二叉樹
- 動態規劃動態規劃
- [動態規劃] 六、最長迴文子串動態規劃
- 運籌優化(七)--動態規劃解析優化動態規劃
- 動態規劃入門——動態規劃與資料結構的結合,在樹上做DP動態規劃資料結構
- 二叉查詢樹概念及實現
- 迴文串問題(動態規劃DP C++)動態規劃C++
- 最優二叉樹(哈夫曼樹)Java實現二叉樹Java
- 動態規劃求解最大子段和 (兩種寫法+還原最優解)動態規劃