動態規划方法生成最優二叉查找樹


1、概念引入

  基於統計先驗知識,我們可統計出一個數表(集合)中各元素的查找概率,理解為集合各元素的出現頻率。比如中文輸入法字庫中各詞條(單字、詞組等)的先驗概率,針對用戶習慣可以自動調整詞頻——所謂動態調頻、高頻先現原則,以減少用戶翻查次數。這就是最優二叉查找樹問題:查找過程中鍵值比較次數最少,或者說希望用最少的鍵值比較次數找到每個關鍵碼(鍵值)。為解決這樣的問題,顯然需要對集合的每個元素賦予一個特殊屬性——查找概率。這樣我們就需要構造一顆最優二叉查找樹。
 
2、問題給出
  n個鍵{a1,a2,a3......an},其相應的查找概率為{p1,p2,p3......pn}。構成最優BST,表示為T 1 n ,求這棵樹的平均查找次數C[1, n](耗費最低)。換言之,如何構造這棵最優BST,使得
C[1, n] 最小。
 
3、分段方法
   
    動態規划法策略是將問題分成多個階段,逐段推進計算,后繼實例解由其直接前趨實例解計算得到。對於最優BST問題,利用減一技術和最優性原則,如果前n-1個節點構成最優BST,加入一個節點 a n 后要求構成規模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]最小。左右子樹遞歸執行此過程。(根的生成過程)
 
 4、遞推計算式
 

 

  5、基本算法如下
  

6、具體實現代碼(其中所有數據都存放在2.txt中,其內容為:

其中5表示有5個節點,其他數據表示各個節點出現的概率;

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #define max 9999
 4 void OptimalBST(int,float*,float**,int**);
 5 void OptimalBSTPrint(int,int,int**);
 6 void main()
 7 {
 8     int i,num;
 9     FILE *point;
10     //所有數據均從2.txt中獲取,2.txt中第一個數據表示節點個數;從第二個數據開始表示各個節點的概率
11     point=fopen("2.txt","r");
12     if(point==NULL)
13     {
14         printf("cannot open 2.txt.\n");
15         exit(-1);
16     }
17     fscanf(point,"%d",&num);
18     printf("%d\n",num);
19     float *p=(float*)malloc(sizeof(float)*(num+1));
20     for(i=1;i<num+1;i++)
21         fscanf(point,"%f",&p[i]);
22     //創建主表;
23     float **c=(float**)malloc(sizeof(float*)*(num+2));
24     for(i=0;i<num+2;i++)
25         c[i]=(float*)malloc(sizeof(float)*(num+1));
26     //創建根表;
27     int **r=(int**)malloc(sizeof(int*)*(num+2));
28     for(i=0;i<num+2;i++)
29         r[i]=(int*)malloc(sizeof(int)*(num+1));
30     //動態規划實現最優二叉查找樹的期望代價求解。。
31     OptimalBST(num,p,c,r);
32     printf("該最優二叉查找樹的期望代價為:%f \n",c[1][num]);
33     //給出最優二叉查找樹的中序遍歷結果;
34     printf("構造成的最優二叉查找樹的中序遍歷結果為:");
35     OptimalBSTPrint(1,4,r);
36 
37 }
38 void OptimalBST(int num,float*p,float**c,int**r)
39 {
40     int d,i,j,k,s,kmin;
41     float temp,sum;
42     for(i=1;i<num+1;i++)//主表和根表元素的初始化
43     {
44     
45         c[i][i-1]=0;
46         c[i][i]=p[i];
47         r[i][i]=i;
48     }
49     c[num+1][num]=0;
50     for(d=1;d<=num-1;d++)//加入節點序列
51     {
52         for(i=1;i<=num-d;i++)
53         {
54             j=i+d;
55             temp=max;
56             for(k=i;k<=j;k++)//找最優根
57             {
58                 if(c[i][k-1]+c[k+1][j]<temp)
59                 {
60                     temp=c[i][k-1]+c[k+1][j];
61                     kmin=k;
62                 }
63             }
64             r[i][j]=kmin;//記錄最優根
65             sum=p[i];
66             for(s=i+1;s<=j;s++)
67                 sum+=p[s];
68             c[i][j]=temp+sum;
69         }
70     }
71 }
72 //采用遞歸方式實現最優根的輸出,最優根都是保存在r[i][j]中的。。。
73 void OptimalBSTPrint(int first,int last,int**r)
74 {
75 
76     int k;
77     if(first<=last)
78     {
79         k=r[first][last];
80         printf("%d  ",k);
81         OptimalBSTPrint(first,k-1,r);
82         OptimalBSTPrint(k+1,last,r);
83     }
84 }

7、最終運行結果:

8、參考文獻:

(1)算法導論

(2)數據結構 嚴蔚敏

(3)網上下載的一個ppt(算法設計與分析,第八章)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM