1. 程式人生 > >第十五章動態規劃之“最優二叉查詢樹”

第十五章動態規劃之“最優二叉查詢樹”

本書從文字翻譯的案例切入,假設把英文翻譯為法文,每個英文單詞為關鍵字,其對應法文為衛星資料。用二叉查詢樹儲存,該怎麼設計這個查詢樹。即使是紅黑樹,查詢的時間複雜度也為O(lgn)即樹的深度。但是因為文章中某個單詞出現的頻率不同,所以可能有些頻率很高的單詞比如the的深度可能很深,而不常見的Aha的深度卻可能很淺。根據直覺,我們應該讓本例中the更加靠近樹根才對(其實即使概率最高也不見得就是樹根)。我們應該讓查詢整個樹的期望次數最小,即構建一個最優二叉查詢樹。一個最優二叉查詢樹的左右子樹的肯定也是最優的。陣列p中是k1~k5的概率。k1到k5是從小到大的順序,即字典序。當找不到要查詢到英文單詞時肯定最終會走到樹葉,每個樹葉出現的概率儲存到陣列q中。我一直沒弄明白這個樹葉的概率作者是怎麼搞出來的。誰知道告訴我一下。

程式一個注意的地方就是浮點數比較,不能直接比較,除非相差很大才行,如果兩個浮點數相等,要是直接使用大於號進行判斷也有可能成立。

程式碼如下:

#include <iostream>
using namespace std;

void OpticalBST(double *p,double *q,int n,double (*e)[6],int (*root)[6])
{
	double w[n+1][6];
	for(int i=1;i<=n+1;i++)
	{
		e[i][i-1]=q[i-1];
		w[i][i-1]=q[i-1]; 
	}
	
	for(int l=1;l<=n;l++)
	{
		for(int i=1;i<=n-l+1;i++)
		{
			int j=i+l-1;
			e[i][j]=1<<30;
			//cout<<e[i][j];system("pause");
			w[i][j]=w[i][j-1]+p[j]+q[j];//子樹i...j的總概率 
			for(int r=i;r<=j;r++)
			{
				double t=e[i][r-1]+e[r+1][j]+w[i][j];
				if(e[i][j]-t>0.0000000001)//不能直接用e[i][j]>t,因為如果e[i][j]和t都是浮點數,即使相等的話,相減可能也不等於0 
				{
					e[i][j]=t;
					root[i][j]=r;
				} 
			}
		}
	}
}

void ConstructOpticalBST(int (*root)[6],int i,int j)
{
	if(i==1&&j==5)
	{
		cout<<"k"<<root[i][j]<<"是根"<<endl;//system("pause");
	}
	if(i<root[i][j]) 
	{
		cout<<"k"<<root[i][root[i][j]-1]<<"是k"<<root[i][j]<<"的左孩子"<<endl;
		ConstructOpticalBST(root,i,root[i][j]-1);
	}
	else
	{
		cout<<"d"<<root[i][j]-1<<"是k"<<root[i][j]<<"的左孩子"<<endl;
	}
	if(root[i][j]<j)
	{
		cout<<"k"<<root[root[i][j]+1][j]<<"是k"<<root[i][j]<<"的右孩子"<<endl;
		ConstructOpticalBST(root,root[i][j]+1,j);
	}
	else
	{
		cout<<"d"<<root[i][j]<<"是k"<<root[i][j]<<"的右孩子"<<endl;
	} 
}

int main()
{
	int n=5;
	double p[6]={0,0.15,0.1,0.05,0.1,0.2};//第一個不用。1~5分別代表k1~k5,並且k1~k5是按從下到大順序排列。K的順序對於輸出整個數很重要。 
	double q[6]={0.05,0.1,0.05,0.05,0.05,0.1}; 
	double e[7][6];
	int root[6][6];
	
	OpticalBST(p,q,n,e,root);
	ConstructOpticalBST(root,1,5);
    system("pause");
    return 0;
}