分治法解决合并排序问题及动态规划解决矩阵连乘和最长公共子序列问题及贪心法解决哈夫曼编码问题.docx
《分治法解决合并排序问题及动态规划解决矩阵连乘和最长公共子序列问题及贪心法解决哈夫曼编码问题.docx》由会员分享,可在线阅读,更多相关《分治法解决合并排序问题及动态规划解决矩阵连乘和最长公共子序列问题及贪心法解决哈夫曼编码问题.docx(19页珍藏版)》请在冰豆网上搜索。
分治法解决合并排序问题及动态规划解决矩阵连乘和最长公共子序列问题及贪心法解决哈夫曼编码问题
分治法解决合并排序问题及动态规划解决矩阵连乘和最长公共子序列问题及贪心法解决哈夫曼编码问题
一、课程设计目的
本次课程设计可以说是我们学完《计算机算法设计与分析》这门课程后的一次综合性训练。
本课程设计的训练的目的是:
1、巩固和掌握计算机算法设计和分析课程的基础知识。
2、培养使用计算机基本算法解决实际问题的能力。
3、提升使用程序设计语言对算法程序的开发、调试和测试能力。
4、对一定的实际问题,能够设计求解算法并分析算法的效率。
5、提高综合运用算法、程序设计语言等能力。
6、掌握文档的书写能力。
二、课程设计内容
1、分治法
(1)合并排序
2、动态规划
(1)矩阵连乘
(2)最长公共子序列
3、贪心法
(1)哈夫曼编码
三、概要设计
1、分治法
基本思想:
将规模为n的问题分解为k个规模较小的子问题,使这些子问题相互独立可分别求解,再将k个子问题的解合并成原问题的解。
如子问题的规模仍很大,则反复分解直到问题小到可直接求解为止。
在分治法中,子问题的解法通常与原问题相同。
(1)合并排序
[问题描述]
将n个元素排成非递减顺序。
[算法思路]
若n为1,算法终止;否则,将n个待排元素分割成k(k=2)个大致相等子集合A,B,对每一个子集合分别递归排序,再将排好序的子集归并为一个集合。
2、动态规划
基本思想:
将问题的求解过程化为多步选择,在每一步选择上,列出各种可能的结果(各子问题的可行解),舍去那些肯定不能成为最优解的局部解。
最后一步得到的解必是最优解。
求解过程多为自底向上,求解过程产生多个选择序列,下一步的选择依赖上一步的结果,总能得到最优解。
(1)矩阵连乘
[问题描述]
给定n个矩阵{A1,…,An},其中Ai与A(i-1)是可相乘的。
确定一个计算次序,使计算矩阵连乘积A1…An所需计算量最少。
例如,三个矩阵连乘,两种计算顺序(A*B)*C,A*(B*C)。
设A为100*1的矩阵,B为1*100的矩阵,C为100*1的矩阵,则D=A*B为100*100的矩阵,需乘法次数为10000,D与C相乘,所需乘法次数为1000000,计算(A*B)*C的总时间长度为1010000。
E=B*C需乘法次数为10000,B*C为1*1的矩阵,E与A相乘,所需乘法数为100,计算A*(B*C)的时间长度只有10100。
计算(A*B)*C时,还需10000个单元来存储A*B,而A*(B*C)计算过程中,只需用1个单元来存储B*C。
[算法思路]
将步骤化为多步,自底向上,先求出矩阵链长为1的最优计算次序,链长为2的最优次序,…
[最优解结构]
设A[1:
n]=A1…An,最优计算次序在Ak和A(k+1)间断开,则总计算量=A[1:
k]的计算量+A[k+1:
n]的计算量+A[1:
k]*A[k+1:
n]则矩阵子链A[1:
k]和A[k+1:
n]的计算次序也必最优。
[递推关系]
设计算A[i:
j]=Ai…Aj所需最少次数乘法为m[i][j],Ai的维数设为matrix[i].row*matrix[i].col。
[构造最优解]
记m[i][j]的断开位置k为s[i][j],在计算出m[i][j]后,可由s[i][j]递归构造相应的最优解。
(2)最长公共子序列
[问题描述]
字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列。
令给定的字符序列x=“x0,x1,…,x(m-1)”,序列y=“y0,y1,…,y(k-1)”是x的子序列,存在x的一个严格递增下标序列,使得对所有的j=0,1,…,k-1,有xij=yj。
[算法思路]
引进一个二维数组c[][],用c[i][j]记录x[i]与y[j]的LCS的长度,b[i][j]记录c[i][j]是通过哪一个子问题的值求得的,以决定搜索的方向。
由自底向上进行递推计算,那么在计算c[i,j]之前c[i-1][j-1],c[i-1][j]与c[i][j-1]均已计算出来。
此时我们根据x[i]=y[j]还是x[i]!
=y[j],就可以计算出c[i][j]。
问题的递归式写成:
3、贪心法
基本思想:
将问题的求解过程看作一系列选择,每次选择都是当前状态下的局部最优解。
每作一次选择后,所求问题会简化为一个规模更小的子问题。
从而通过每一步的最优解逐步达到整体最优解。
(1)哈夫曼编码
[问题描述]
通讯过程中需将传输的信息转换为二进制码。
由于英文字母使用频率不同,若频率高的字母对应短的编码,频率低的字母对应长的编码,传输的数据总量就会降低。
要求找到一个编码方案,使传输的数据量最少。
哈夫曼编码就是一种最佳编码方案。
[算法思路]
1)以n个字母为结点构成n棵仅含一个点的二叉树集合,字母的频率即为结点的权。
2)每次从二叉树集合中找出两个权最小者合并为一棵二叉树:
增加一个根结点将这两棵树作为左右子树。
新树的权为两棵子树的权之和。
3)反复进行步骤2)直到只剩一棵树为止。
四、详细设计与实现
1、合并排序
例:
序列分解过程:
{8473652}
{8473}{652}
{84}{73}{65}{2}
初始序列aa[0]a[1]a[2]a[3]a[4]a[5]a[6]
{8}{4}{7}{3}{6}{5}{2}
排序后归并到b{48}{73}{65}{2}
复制到a{48}{73}{65}{2}
排序后归并到b{3478}{256}
复制到a{3478}{256}
排序后归并到b{2345678}
复制到a{2345678}
最终结果为:
{2345678}
C++实现代码为:
#include
usingnamespacestd;
voidMerge(inta[],intb[],intl,intm,intr)
{//合并a[l:
m]和b[m+1:
r]存入到b[l:
r]中
inti=l,j=m+1,k=l;
while((i<=m)&&(j<=r))
if(a[i]<=a[j])b[k++]=a[i++];
elseb[k++]=a[j++];
if(i>m)
for(intq=j;q<=r;q++)
b[k++]=a[q];
else
for(intq=i;q<=m;q++)
b[k++]=a[q];
}
voidCopy(inta[],intb[],ints,intn)
{
for(inti=s;i<=n;i++)
a[i]=b[i];
}
voidMergeSort(inta[],intleft,intright)
{
inti;
if(left{
i=(left+right)/2;//取中点
intb[100];
MergeSort(a,left,i);//递归调用分别对两个字问题排序
MergeSort(a,i+1,right);
Merge(a,b,left,i,right);//合并到数组b
Copy(a,b,left,right);//复制回数组a
}
}
intmain()
{
inta[100];
intn,i;
cout<<"输入元素个数n:
";
cin>>n;
cout<<"输入一维数组a["<";
for(i=0;icin>>a[i];
MergeSort(a,0,n-1);
cout<<"输出排序为:
";
for(i=0;icout<cout<return0;
}
运行截图:
2、矩阵连乘
例:
A1
A2
A3
A4
A5
A6
30*35
35*15
15*5
5*10
10*20
20*25
结果为:
((A1(A2A3))((A4A5)A6))
C++实现代码:
#include
#defineMAX100
usingnamespacestd;
structMatrix//矩阵
{
introw;//矩阵行数
intcol;//矩阵列数
};
//矩阵
Matrixmatrix[MAX];
//m[i][j]存储Ai到Aj的最小乘法次数
intm[MAX][MAX];
//s[i][j]存储Ai到Aj之间加括号的位置
ints[MAX][MAX];
//矩阵个数
intn;
voidMaxtrixChain(Matrixmatrix[MAX],intn,intm[MAX][MAX],ints[MAX][MAX])
{//计算m[][]和s[][]
for(intr=2;r<=n;r++)
for(inti=1;i<=n-r+1;i++)
{
intj=i+r-1;
m[i][j]=m[i+1][j]+matrix[i].row*matrix[i].col*matrix[j].col;
s[i][j]=i;
for(intk=i+1;k{
intt=m[i][k]+m[k+1][j]+matrix[i].row*matrix[k].col*matrix[j].col;
if(t{
m[i][j]=t;
s[i][j]=k;
}
}
}
}
voidmatrixMultiply(Matrixmatrix[MAX],intn)
{
boolflag=false;//标识矩阵的阶数是否要重新输入
inti;
cout<<"请输入每个矩阵行数与列数:
"<for(i=1;i<=n;i++)
{
cout<<"A"<
";
cin>>matrix[i].row;
cout<<"A"<
";
cin>>matrix[i].col;
}
//检查Ai的列数是否等于Ai+1的行数
for(i=1;i{
if(matrix[i].col!
=matrix[i+1].row)
{
cout<<"输入的矩阵不可乘,请重新输入!
"<flag=true;
break;
}
}
if(flag)
{
matrixMultiply(matrix,n);
}
}
//打印加括号后的
voidtraceback(inti,intj)
{
if(i==j)
{
cout<<"A"<
}
else
{
cout<<"(";
traceback(i,s[i][j]);
traceback(s[i][j]+1,j);
cout<<")";
}
}
voidmain()
{
//变量m,s初始化
memset(m,0,sizeof(m));
memset(s,0,sizeof(s));
cout<<"请输入矩阵的个数:
"<cin>>n;
matrixMultiply(matrix,n);
MaxtrixChain(matrix,n,m,s);
cout<<"加括号之后:
"<traceback(1,n);
cout<}
运行截图:
3、最长公共子序列
例:
x="cbwdabh"y="sdabwyz"
c[i][j]:
b[i][j]:
最终结果为:
dab
C++实现代码:
#include
usingnamespacestd;
#defineMAX100
voidLCSLength(char*x,char*y,intm,intn,intc[MAX][MAX],intb[MAX][MAX])
{//用b[][]对c[][]中的元素分成三类
inti,j;
for(i=0;i<=m;i++)
c[i][0]=0;
for(j=1;j<=n;j++)
c[0][j]=0;
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
if(x[i-1]==y[j-1])//第一类c[][]中的元素
{
c[i][j]=c[i-1][j-1]+1;
b[i][j]=1;
}
elseif(c[i-1][j]>=c[i][j-1])//第二类c[][]中元素
{
c[i][j]=c[i-1][j];
b[i][j]=2;
}
else//第三类c[][]中元素
{
c[i][j]=c[i][j-1];
b[i][j]=3;
}
}
}
}
voidLCS(intb[MAX][MAX],char*x,inti,intj)
{
if(i==0||j==0)
return;
if(b[i][j]==1)//输出第一类元素对应的x[]
{
LCS(b,x,i-1,j-1);
cout<}
elseif(b[i][j]==2)//输出第二类元素对应的x[]
LCS(b,x,i-1,j);
else//输出第三类元素对应的x[]
LCS(b,x,i,j-1);
}
voidmain()
{
charx[MAX];
chary[MAX];
cout<<"输入字符串x:
"<cin>>x;
cout<<"输入字符串y:
"<cin>>y;
intb[MAX][MAX];
intc[MAX][MAX];
intm,n;
m=strlen(x);
n=strlen(y);
LCSLength(x,y,m,n,c,b);
cout<<"最长公共子序列为:
"<LCS(b,x,m,n);
cout<}
运行截图:
4、Hufman编码
例:
abcdef
频率:
4513121695
哈夫曼树为:
结果为:
a:
0
b:
101
c:
100
d:
111
e:
1101
f:
1100
C++实现代码:
#include
#include
#defineMAX32767;
usingnamespacestd;
typedefstruct//定义哈夫曼结点结构体
{
intweight;//权值
intflag;//标识是否有父母结点
intparent;//父母结点
intlchild;//左孩子结点
intrchild;//右孩子结点
}hnodetype;
typedefstruct//定义哈夫曼编码结构体
{
intbit[10];//定义编码数组
intstart;
charleaf;
}hcodetype;
voidhuffman(charcha[],intm[],intn)
{
inti,j,m1,m2,x1,x2,c,p;
hnodetype*huffnode=newhnodetype[2*n-1];//动态分配结构体空间
hcodetype*huffcode=newhcodetype[n],cd;//定义
for(i=0;i<2*n-1;i++)//对哈夫曼结点结构体初始化
{
huffnode[i].weight=0;
huffnode[i].parent=0;
huffnode[i].flag=0;
huffnode[i].lchild=-1;
huffnode[i].rchild=-1;
}
for(i=0;i{
huffnode[i].weight=m[i];//给哈夫曼结点赋权值
huffcode[i].leaf=cha[i];//给哈夫曼编码叶子赋字符
}
for(i=0;i{
m1=m2=MAX;
x1=x2=0;
for(j=0;j{
if(huffnode[j].weight<=m1&&huffnode[j].flag==0)
{
m2=m1;
x2=x1;
m1=huffnode[j].weight;
x1=j;
}
elseif(huffnode[j].weight<=m2&&huffnode[j].flag==0)
{
m2=huffnode[j].weight;
x2=j;
}
}
huffnode[x1].parent=n+i;
huffnode[x2].parent=n+i;
huffnode[x1].flag=1;
huffnode[x2].flag=1;
huffnode[n+i].weight=huffnode[x1].weight+huffnode[x2].weight;
huffnode[n+i].lchild=x1;
huffnode[n+i].rchild=x2;
}
for(i=0;i{
cd.start=n-1;
c=i;
p=huffnode[c].parent;
while(p!
=0)//构建哈夫曼编码权值
{
if(huffnode[p].lchild==c)
cd.bit[cd.start]=0;
else
cd.bit[cd.start]=1;
cd.start--;
c=p;
p=huffnode[c].parent;
}
cout<";
for(j=cd.start+1;j{
huffcode[i].bit[j]=cd.bit[j];
cout<}
cout<huffcode[i].start=cd.start;
}
delete[]huffcode;//释放空间
delete[]huffnode;//释放空间
}
voidmain()
{
inti=0,n,m[100],k;
charcha[100];
cout<<"输入的总字符n:
";
cin>>n;
k=n;
for(i=0;i{
cout<<"第"<
";
cin>>cha[i];
cout<<"字符"<";
cin>>m[i];
}
cout<<"每个字符的哈夫曼编码是:
"<huffman(cha,m,k);
}
运行截图:
五、总结
经过两个星期的计算机算法设计与分析课程设计,终于顺利完成这次课程设计。
通过这次课程设计,收获颇丰。
1、对算法理解更深
通过该课程设计,掌握了计算机算法程序,以及算法的运行原理。
通过VC++6.0编译器编译算法程序,将书上的算法实现在计算机上,把原来以为很深奥的书本知识变的更为简单,对算法原理有更深的理解。
2、对该算法理论在实践中的应用有深刻的理解
通过把算法程序在计算机上实现,知道和理解了算法在计算机中是怎样执行的,对算法理论在实践中的应用有深刻的理解。
3、激发了学习的积极性
通过该课程设计,全面系统的理解了算法的一般原理和基本实现方法。
把死板的课本知识变得生动有趣,激发了学习的积极性。
把学过的计算机算法的知识得到了强化,能够把课堂上学的知识通过自己设计的程序表示出来,加深了对算法理论知识的理解。
以前对与计算机算法的认识是模糊的,概念上的,现在通过自己动手做实验,从实践上认识了算法的作用,对计算机编程能力也有了进一步的提升。
4、理解了该知识点以及学科之间的融合渗透
本次课程设计程序部分是用C++语言编写的,把《数据结构》《计算机算法设计与分析》《C++程序设计》三门学科联系起来,把各个学科之间的知识融合起来,把各门课程的知识联系起来,对计算机学科知识的认识更加深刻,进一步加深了对这三门课程的认识。