用分治法解决快速排序问题及用动态规划法解决最优二叉搜索树问题及用回溯法解决图的着色问题一、课程设计目的:《计算机算法设计与分析》这门课程是一门实践性非常强的课程,要求我们能够将所学的算法应用到实际中,灵活解决实际问题。
通过这次课程设计,能够培养我们独立思考、综合分析与动手的能力,并能加深对课堂所学理论和概念的理解,可以训练我们算法设计的思维和培养算法的分析能力。
二、课程设计内容:1、分治法:(2)快速排序;2、动态规划:(4)最优二叉搜索树;3、回溯法:(2)图的着色。
三、概要设计:分治法—快速排序:分治法的基本思想是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。
递归地解这些子问题,然后将各个子问题的解合并得到原问题的解。
分治法的条件:(1)该问题的规模缩小到一定的程度就可以容易地解决;(2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;(3) 利用该问题分解出的子问题的解可以合并为该问题的解;(4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
抽象的讲,分治法有两个重要步骤:(1)将问题拆开;(2)将答案合并;● 动态规划—最优二叉搜索树:动态规划的基本思想是将问题分解为若干个小问题,解子问题,然后从子问题得到原问题的解。
设计动态规划法的步骤:(1)找出最优解的性质,并刻画其结构特征; (2)递归地定义最优值(写出动态规划方程); (3)以自底向上的方式计算出最优值;(4)根据计算最优值时得到的信息,构造一个最优解。
● 回溯法—图的着色回溯法的基本思想是确定了解空间的组织结构后,回溯法就是从开始节点(根结点)出发,以深度优先的方式搜索整个解空间。
这个开始节点就成为一个活结点,同时也成为当前的扩展结点。
在当前的扩展结点处,搜索向纵深方向移至一个新结点。
这个新结点就成为一个新的或节点,并成为当前扩展结点。
如果在当前的扩展结点处不能再向纵深方向移动,则当前的扩展结点就成为死结点。
换句话说,这个节点,这个结点不再是一个活结点。
此时,应往回(回溯)移动至最近一个活结点处,并使这个活结点成为当前的扩展结点。
回溯法即以这种工作方式递归的在解空间中搜索,直到找到所要求的解或解空间中以无活结点为止。
四、详细设计与实现:● 分治法—快速排序快速排序是基于分治策略的另一个排序算法。
其基本思想是,对于输入的子数组[]r p a :,按以下三个步骤进行排序:(1)、分解(divide) 以元素[]p a 为基准元素将[]r p a :划分为三段[]1:-q p a ,[]q a 和,[]r q a :1+使得[]1:-q p a 中任何一个元素都小于[]q a ,而[]r q a :1+中任何一个元素大于等于[]q a ,下标在划分过程中确定。
(2)、递归求解(conquer) 通过递归调用快速排序算法分别对[]1:-q p a 和[]r q a :1+进行排序。
(3)、合并(merge) 由于[]1:-q p a 和[]r q a :1+的排序都是在原位置进行的,所以不必进行任何合并操作就已经排好序了。
算法实现题: 现将数列{23 21 34 45 65 76 86 46 30 39 89 20 2 3 8 47 38 54 59 40}进行快速排序。
源程序如下:#include <iostream>using namespace std;#define size 20int partition(int data[],int p,int r){int n=data[p],i=p+1,j=r,temp;//将<n的元素交换到左边区域//将>n的元素交换到右边区域while(true){while(data[i]<n) ++i;while(data[j]>n) --j;if(i>=j)break;temp=data[i]; data[i]=data[j]; data[j]=temp;}data[p]=data[j];data[j]=n;return j;}void quick_sort(int data[],int p,int r){if(p>=r)return;int q=partition(data,p,r);quick_sort(data,p,q-1); //对左半段排序quick_sort(data,q+1,r); //对右半段排序}int main(){int i,n,data[size];printf("请输入要排列的数目(<=20):");scanf("%d",&n);printf("请输入要排列的数列:\n");for(i=0;i<n;++i)scanf("%d",&data[i]);quick_sort(data,0,n-1);printf("排列后的数列为:\n");for(i=0;i<n;++i)printf( "%d ",data[i]);printf("\n");return 0;}运行结果如下:图1 动态规划—最优二叉搜索树1、最优二叉搜索树问题描述和分析:设{}n x x x S ,,,21 =是有序集,且n x x x <<< 21,表示有序集S 的二叉搜索树利用二叉树的结点存储有序集中的元素。
它具有下述性质:存储于每个结点中的元素x 大于其左子树中任一结点所存储的元素,小于其右子树中任一结点所存储的元素。
二叉树的叶结点是形如()1,+i i x x 的开区间,在表示S 的二叉搜索树中搜索元素x ,返回的结果有两种情况:(1)在二叉搜索树的内结点中找到i x x =。
(2)在二叉搜索树的叶结点中确定()1,+∈i i x x x 。
设在第(1)中情形中找到元素i x x =的概率为;在第(2)种情形中确定()1,+∈i i x x x 的概率为。
其中约定+∞=-∞=+10,n x x 。
显然有:;1,0;0,0n j b n i a j i ≤≤≥≤≤≥11=+∑∑==nj j n i i b a()n n a b a b a ,,,,,110 称为集合S 的存取概率分布。
在表示S 的二叉搜索树T 中,设存储元素的结点深度为;叶结点()1,+j j x x 的结点深度为,则:()∑∑==++=nj jj i n i i d a c b p 011表示在二叉搜索树T 中进行一次搜索所需要的平均比较次数,p 又成为二叉搜索树T 的平均路长。
在一般情况下,不同的二叉搜索树的平均路长是不相同的。
最优二叉搜索树问题是对于有序集S 及其存取概率分布()n n a b a b a ,,,,,110 ,在所有表示有序集S 的二叉搜索树中找到一棵具有最小平均路长的二叉搜索树。
2、最优子结构性质:二叉搜索树T 的一棵含有结点j i x x ,, 和叶结点()()11,,,,+-j j i i x x x x 的子树可以看作是有序集{}j i x x ,, 关于全集合{}11,,+-j i x x 的一棵二叉搜索树,其存取概率为以下的条件概率:()j k i w b b ij k k ≤≤=/ ()j h i w a a ij h h ≤≤-=1/式中,n j i a b b a w j j i i ij ≤≤≤++++=-1,1 。
设是有序集{}j i x x ,, 关于存取概率{}j j i i a b b a ,,,,1 -的一棵最优二叉搜索树,其平均路长为。
的根结点存储元素。
其左右子树和的平均路长分别为和。
由于和中结点深度是它们在中的结点深度减1,故有:r j m l m i j i j i j i p w p w w p w ,11,,,,+-++=由于是关于集合{}1,,-m i x x 的一棵二叉搜索树,故1,-≥m i l p p 。
若1,->m i l p p ,则用1,-m i T 替换可得到平均路长比更小的二叉搜索树。
这与是最优二叉搜索树矛盾。
故是一棵最优二叉搜索树。
同理可证也是一棵最优二叉搜索树。
因此最优二叉搜索树问题具有最优子结构性质。
3、递归计算最优值:最优二叉搜索树的平均路长为,则所求的最优值为n p ,1。
由最优二叉搜索树问题的最优子结构性质可建立计算的递归式如下:{}j i p w p w w p w j k j k k i k i jk i j i j i j i ≤++=++--≤≤,,1,11,1,,,,m in初始时,n i p i i ≤≤=-1,01,。
记j i j i p w ,,为()j i m ,,则()n n n p p w n m ,1,1,1,1==为所求的最优值。
计算()j i m ,的递归式为:()()(){}j i j k m k i m w j i m jk i j i ≤++-+=≤≤,,11,,m in ,()n i i i m ≤≤=-1,01,据此,可设计出解最优二叉搜索树问题的动态规划算法。
算法实现题: 给出标识符集{1,2,3}={do,if,stop}存取概率,若b1=0.4 b2=0.2b3=0.05 a0=0.2 a1=0.05 a2=0.05 a3=0.05构造一棵最优二叉搜索树 源程序如下: #include<iostream> using namespace std;void OptimalBinarySearchTree(float a[],float b[],int n,float m[][20],int s[][20],float w[][20]) { //求解最优值的方法 int i,r,k;float t;for(i=0;i<=n;i++){w[i+1][i]=a[i]; //搜索不到的点,最优解为0m[i+1][i]=0;}for(r=0;r<n;r++)for(i=1;i<=n-r;i++){int j=i+r; //左子树为空w[i][j]=w[i][j-1]+a[j]+b[j];m[i][j]=m[i+1][j];s[i][j]=i;for(k=i+1;k<=j;k++){t=m[i][k-1]+m[k+1][j];if(t<m[i][j]){ //以k为根节点,左子树不为空m[i][j]=t;s[i][j]=k;}}m[i][j]+=w[i][j]; }for(i=1;i<=n;i++)for(int j=1;j<=n;j++)cout<<"s["<<i<<"]["<<j<<"]="<<s[i][j]<<endl;}void print(int i,int j,int s[][20],int S[]) //递归输出结果{if(j>=i){int k=s[i][j];cout<<"(";print(i,k-1,s,S);cout<<")";cout<<" "<<S[k]<<" ";cout<<"(";print(k+1,j,s,S);cout<<")";}}int main(){ //主函数int n,i;float a[20],b[20],m[20][20],w[20][20];int s[20][20],S[20];cout<<"请输入有序集元素的个数n:"<<endl;cin>>n;cout<<"请输入有序集各元素的值S[i](一共"<<n<<"个):"<<endl;for(i=1;i<=n;i++)cin>>S[i];cout<<"请输入概率数组a的各元素的值a[i](一共"<<n+1<<"个):"<<endl;for(i=0;i<=n;i++)cin>>a[i];cout<<"请输入概率数组b的各元素的值b[i](一共"<<n<<"个):"<<endl;for(i=1;i<=n;i++)cin>>b[i];OptimalBinarySearchTree(a,b,n,m,s,w);cout<<"最优值即平均步长为:"<<m[1][n]<<endl;}运行结果如下:图2回溯法—图的着色 1、图的m 着色问题描述:给定无向连通图G 和m 种不同的颜色。