0020算法笔记动态规划最优二叉搜索树问题.docx

上传人:b****8 文档编号:10866694 上传时间:2023-02-23 格式:DOCX 页数:15 大小:172.98KB
下载 相关 举报
0020算法笔记动态规划最优二叉搜索树问题.docx_第1页
第1页 / 共15页
0020算法笔记动态规划最优二叉搜索树问题.docx_第2页
第2页 / 共15页
0020算法笔记动态规划最优二叉搜索树问题.docx_第3页
第3页 / 共15页
0020算法笔记动态规划最优二叉搜索树问题.docx_第4页
第4页 / 共15页
0020算法笔记动态规划最优二叉搜索树问题.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

0020算法笔记动态规划最优二叉搜索树问题.docx

《0020算法笔记动态规划最优二叉搜索树问题.docx》由会员分享,可在线阅读,更多相关《0020算法笔记动态规划最优二叉搜索树问题.docx(15页珍藏版)》请在冰豆网上搜索。

0020算法笔记动态规划最优二叉搜索树问题.docx

0020算法笔记动态规划最优二叉搜索树问题

0020算法笔记——【动态规划】最优二叉搜索树问题

    1、问题描速:

  

   设S={x1,x2,···,xn}是一个有序集合,且x1,x2,···,xn表示有序集合的二叉搜索树利用二叉树的顶点存储有序集中的元素,而且具有性质:

存储于每个顶点中的元素x大于其左子树中任一个顶点中存储的元素,小于其右子树中任意顶点中存储的元素。

二叉树中的叶顶点是形如(xi,xi+1)的开区间。

在表示S的二叉搜索树中搜索一个元素x,返回的结果有两种情形:

  

(1)在二叉树的内部顶点处找到:

x=xi

  

(2)在二叉树的叶顶点中确定:

x∈(xi ,xi+1)

  设在情形

(1)中找到元素x=xi的概率为bi;在情形

(2)中确定x∈(xi ,xi+1)的概率为ai。

其中约定x0=-∞,xn+1=+∞,有

   

  集合{a0,b1,a1,……bn,an}称为集合S的存取概率分布。

   

  最优二叉搜索树:

在一个表示S的二叉树T中,设存储元素xi的结点深度为ci;叶结点(xj,xj+1)的结点深度为dj。

   

   注:

在检索过程中,每进行一次比较,就进入下面一层,对于成功的检索,比较的次数就是所在的层数加1。

对于不成功的检索,被检索的关键码属于那个外部结点代表的可能关键码集合,比较次数就等于此外部结点的层数。

对于图的内结点而言,第0层需要比较操作次数为1,第1层需要比较2次,第2层需要3次。

   p表示在二叉搜索树T中作一次搜索所需的平均比较次数。

P又称为二叉搜索树T的平均路长,在一般情况下,不同的二叉搜索树的平均路长是不同的。

对于有序集S及其存取概率分布(a0,b1,a1,……bn,an),在所有表示有序集S的二叉搜索树中找出一棵具有最小平均路长的二叉搜索树。

   

   设Pi是对ai检索的概率。

设qi是对满足ai

    对于有n个关键码的集合,其关键码有n!

种不同的排列,可构成的不同二叉搜索树有

棵。

(n个结点的不同二叉树,卡塔兰数)。

如何评价这些二叉搜索树,可以用树的搜索效率来衡量。

例如:

标识符集{1,2,3}={do,if,stop}可能的二分检索树为:

   若P1=0.5,P2=0.1,P3=0.05,q0=0.15,q1=0.1,q2=0.05,q3=0.05,求每棵树的平均比较次数(成本)。

   

   Pa(n)=1×p1+2×p2+3×p3+1×q0+2×q1+3×(q2+q3) =1×0.5+2×0.1+3×0.05+1×0.05+2×0.1+3×(0.05+0.05) =1.5

   Pb(n)=1×p1+2×p3+3×p2+1×q0+2×q3+ 3×(q1+q2) =1×0.5+2×0.05+3×0.1+1×0.15+2×0.05+3×(0.1+0.05) =1.6

   Pc(n)=1×p2+2×(p1+ p3)+2×(q0+q1+q2+q3) =1×0.1+2×(0.5+0.05)+2×(0.15+0.1+0.05+0.05) =1.9

   Pd(n)=1×p3+2×p1+3×p2+1×q3+2×q0+3×(q1+q2) =1×0.05+2×0.5+3×0.1+1×0.05+2×0.15+3×(0.1+0.05) =2.15

   Pe(n)=1×p3+2×p2+3×p1+1×q3+2×q2+3×(q0+q1) =1×0.05+2×0.1+3×0.5+1×0.05+2×0.15+3×(0.15+0.1) =2.85

   因此,上例中的最小平均路长为Pa(n)=1.5。

   可以得出结论:

结点在二叉搜索树中的层次越深,需要比较的次数就越多,因此要构造一棵最小二叉树,一般尽量把搜索概率较高的结点放在较高的层次。

    2、最优子结构性质:

   假设选择k为树根,则1,2,…,k-1和a0,a1,…,ak-1 都将位于左子树L上,其余结点(k+1,…,n和ak,ak+1,…,an)位于右子树R上。

设COST(L)和COST(R)分别是二分检索树T的左子树和右子树的成本。

则检索树T的成本是:

P(k)+COST(L)+COST(R)+……。

若T是最优的,则上式及COST(L)和COST(R)必定都取最小值。

  证明:

二叉搜索树T的一棵含有顶点xi ,···,xj和叶顶点(xi-1 ,xi ),···,(xj ,xj+1)的子树可以看作是有序集{xi ,···,xj}关于全集为{xi-1 ,xj+1 }的一棵二叉搜索树(T自身可以看作是有序集)。

根据S的存取分布概率,在子树的顶点处被搜索到的概率是:

{xi ,···,xj}的存储概率分布为{ai-1,bi,…,bj,aj },其中,ah,bk分别是下面的条件概率:

   设Tij是有序集{xi ,···,xj}关于存储概率分布为{ai-1,bi,…,bj,aj}的一棵最优二叉搜索树,其平均路长为pij,Tij的根顶点存储的元素xm,其左子树Tl和右子树Tr的平均路长分别为pl和pr。

由于Tl和Tr中顶点深度是它们在Tij中的深度减1,所以得到:

   由于Ti是关于集合{xi ,···,xm-1}的一棵二叉搜索树,故Pl>=Pi,m-1。

若Pl>Pi,m-1,则用Ti,m-1替换Tl可得到平均路长比Tij更小的二叉搜索树。

这与Tij是最优二叉搜索树矛盾。

故Tl是一棵最优二叉搜索树。

同理可证Tr也是一棵最优二叉搜索树。

因此最优二叉搜索树问题具有最优子结构性质。

    3、递推关系:

   根据最优二叉搜索树问题的最优子结构性质可建立计算pij的递归式如下:

   

初始时:

   记wi,j pi,j为m(i,j),则m(1,n)=w1,n p1,n=p1,n为所求的最优值。

计算m(i,j)的递归式为:

   

    4、求解过程:

  1)没有内部节点时,构造T[1][0],T[2][1],T[3][2]……,T[n+1][n]

  2)构造只有1个内部结点的最优二叉搜索树T[1][1],T[2][2]…,T[n][n],可以求得m[i][i]同时可以用一个数组存做根结点元素为:

s[1][1]=1,s[2][2]=2…s[n][n]=n

  3)构造具有2个、3个、……、n个内部结点的最优二叉搜索树。

  ……

   r(起止下标的差)

  0 T[1][1],T[2][2]   ,…,  T[n][n],

  1 T[1][2],T[2][3],…,T[n-1][n],

  2 T[1][3],T[2][4],…,T[n-2][n],

  ……

  r T[1][r+1],T[2][r+2],…,T[i][i+r],…,T[n-r][n]

  ……

  n-1 T[1][n] 

  具体代码如下:

   

[cpp] viewplain copy

1.//3d11-1 最优二叉搜索树 动态规划  

2.#include "stdafx.h"  

3.#include    

4.using namespace std;  

5.  

6.const int N = 3;  

7.  

8.void OptimalBinarySearchTree(double a[],double b[],int n,double **m,int **s,double **w);  

9.void Traceback(int n,int i,int j,int **s,int f,char ch);  

10.  

11.int main()  

12.{  

13.    double a[] = {0.15,0.1,0.05,0.05};  

14.    double b[] = {0.00,0.5,0.1,0.05};  

15.  

16.    cout<<"有序集的概率分布为:

"<

17.    for(int i=0; i

18.    {  

19.        cout<<"a"<

20.    }  

21.  

22.    double **m = new double *[N+2];  

23.    int **s = new int *[N+2];  

24.    double **w =new double *[N+2];  

25.  

26.    for(int i=0;i

27.    {    

28.        m[i] = new double[N+2];    

29.        s[i] = new int[N+2];    

30.        w[i] = new double[N+2];    

31.    }   

32.  

33.    OptimalBinarySearchTree(a,b,N,m,s,w);  

34.    cout<<"二叉搜索树最小平均路长为:

"<

35.    cout<<"构造的最优二叉树为:

"<

36.    Traceback(N,1,N,s,0,'0');  

37.  

38.    for(int i=0;i

39.    {    

40.        delete m[i];  

41.        delete s[i];  

42.        delete w[i];  

43.    }   

44.    delete[] m;  

45.    delete[] s;  

46.    delete[] w;  

47.    return 0;  

48.}  

49.  

50.void OptimalBinarySearchTree(double a[],double b[],int n,double **m,int **s,double **w)  

51.{  

52.    //初始化构造无内部节点的情况  

53.    for(int i=0; i<=n; i++)  

54.    {  

55.        w[i+1][i] = a[i];  

56.        m[i+1][i] = 0;  

57.    }  

58.  

59.    for(int r=0; r

60.    {  

61.        for(int i=1; i<=n-r; i++)//i为起始元素下标  

62.        {  

63.            int j = i+r;//j为终止元素下标  

64.  

65.            //构造T[i][j] 填写w[i][j],m[i][j],s[i][j]  

66.            //首选i作为根,其左子树为空,右子树为节点  

67.            w[i][j]=w[i][j-1]+a[j]+b[j];  

68.            m[i][j]=m[i+1][j];  

69.            s[i][j]=i;  

70.  

71.            //不选i作为根,设k为其根,则k=i+1,……j  

72.            //左子树为节点:

i,i+1……k-1,右子树为节点:

k+1,k+2,……j  

73.            for(int k=i+1; k<=j; k++)  

74.            {  

75.                double t = m[i][k-1]+m[k+1][j];  

76.  

77.                if(t

78.                {  

79.                    m[i][j]=t;  

80.                    s[i][j]=k;//根节点元素  

81.                }  

82.            }  

83.            m[i][j]+=w[i][j];  

84.        }  

85.    }  

86.}  

87.  

88.void Traceback(int n,int i,int j,int **s,int f,char ch)  

89.{  

90.    int k=s[i][j];  

91.    if(k>0)  

92.    {  

93.        if(f==0)  

94.        {  

95.            //根  

96.            cout<<"Root:

"<

j):

("<

97.        }  

98.        else  

99.        {  

100.            //子树  

101.            cout<

"<

j):

("<

102.        }  

103.  

104.        int t = k-1;  

105.        if(t>=i && t<=n)  

106.        {  

107.            //回溯左子树  

108.            Traceback(n,i,t,s,k,'L');  

109.        }  

110.        t=k+1;  

111.        if(t<=j)  

112.        {  

113.            //回溯右子树  

114.            Traceback(n,t,j,s,k,'R');  

115.        }  

116.    }  

117.}  

     4、构造最优解:

  算法OptimalBinarySearchTree中用s[i][j]保存最优子树T(i,j)的根节点中的元素。

当s[i][n]=k时,xk为所求二叉搜索树根节点元素。

其左子树为T(1,k-1)。

因此,i=s[1][k-1]表示T(1,k-1)的根节点元素为xi。

依次类推,容易由s记录的信息在O(n)时间内构造出所求的最优二叉搜索树。

    5、复杂度分析与优化:

  算法中用到3个数组m,s和w,故所需空间复杂度为O(n^2)。

算法的主要计算量在于计算

对于固定的r,它需要的计算时间O(j-i+1)=O(r+1)。

因此算法所耗费的总时间为:

事实上,由《动态规划加速原理之四边形不等式》可以得到:

而此状态转移方程的时间复杂度为O(n^2)。

由此,对算法改进后的代码如下:

[cpp] viewplain copy

1.//3d11-1 最优二叉搜索树 动态规划加速原理 四边形不等式  

2.#include "stdafx.h"  

3.#include    

4.using namespace std;  

5.  

6.const int N = 3;  

7.  

8.void OptimalBinarySearchTree(double a[],double b[],int n,double **m,int **s,double **w);  

9.void Traceback(int n,int i,int j,int **s,int f,char ch);  

10.  

11.int main()  

12.{  

13.    double a[] = {0.15,0.1,0.05,0.05};  

14.    double b[] = {0.00,0.5,0.1,0.05};  

15.  

16.    cout<<"有序集的概率分布为:

"<

17.    for(int i=0; i

18.    {  

19.        cout<<"a"<

20.    }  

21.  

22.    double **m = new double *[N+2];  

23.    int **s = new int *[N+2];  

24.    double **w =new double *[N+2];  

25.  

26.    for(int i=0;i

27.    {    

28.        m[i] = new double[N+2];    

29.        s[i] = new int[N+2];    

30.        w[i] = new double[N+2];    

31.    }   

32.  

33.    OptimalBinarySearchTree(a,b,N,m,s,w);  

34.    cout<<"二叉搜索树最小平均路长为:

"<

35.    cout<<"构造的最优二叉树为:

"<

36.    Traceback(N,1,N,s,0,'0');  

37.  

38.    for(int i=0;i

39.    {    

40.        delete m[i];  

41.        delete s[i];  

42.        delete w[i];  

43.    }   

44.    delete[] m;  

45.    delete[] s;  

46.    delete[] w;  

47.    return 0;  

48.}  

49.  

50.void OptimalBinarySearchTree(double a[],double b[],int n,double **m,int **s,double **w)  

51.{  

52.    //初始化构造无内部节点的情况  

53.    for(int i=0; i<=n; i++)  

54.    {  

55.        w[i+1][i] = a[i];  

56.        m[i+1][i] = 0;  

57.        s[i+1][i] = 0;  

58.    }  

59.  

60.    for(int r=0; r

61.    {  

62.        for(int i=1; i<=n-r; i++)//i为起始元素下标  

63.        {  

64.            int j = i+r;//j为终止元素下标  

65.            int i1 = s[i][j-1]>i?

s[i][j-1]:

i;  

66.            int j1 = s[i+1][j]>i?

s[i+1][j]:

j;  

67.  

68.            //构造T[i][j] 填写w[i][j],m[i][j],s[i][j]  

69.            //首选i作为根,其左子树为空,右子树为节点  

70.            w[i][j]=w[i][j-1]+a[j]+b[j];  

71.            m[i][j]=m[i][i1-1]+m[i1+1][j];  

72.            s[i][j]=i1;  

73.  

74.            //不选i作为根,设k为其根,则k=i+1,……j  

75.            //左子树为节点:

i,i+1……k-1,右子树为节点:

k+1,k+2,……j  

76.            for(int k=i1+1; k<=j1; k++)  

77.            {  

78.                double t = m[i][k-1]+m[k+1][j];  

79.  

80.                if(t

81.                {  

82.                    m[i][j]=t;  

83.                    s[i][j]=k;//根节点元素  

84.                }  

85.            }  

86.            m[i][j]+=w[i][j];  

87.        }  

88.    }  

89.}  

90.  

91.void Traceback(int n,int i,int j,int **s,int f,char ch)  

92.{  

93.    int k=s[i][j];  

94.    if(k>0)  

95.    {  

96.        if(f==0)  

97.        {  

98.            //根  

99.           

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 幼儿教育 > 育儿知识

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1