简介:本文是博主在复习算法设计与分析的笔记,参考了北大算法设计与分析以及王晓东编著的《计算机算法设计与分析》第四版相关内容,如有错误,欢迎指正。
设计技术
分治
使用条件 | 规约为独立求解子问题 |
---|---|
设计步骤 | 规约方法,初始子问题的计算,子问题解的综合方法,注意子问题划分均衡,类型相同 |
递归算法分析 | 求解递推方程 |
改进途径 | 减少子问题数,预处理 |
典型问题 | 二分检索,归并排序,芯片测试,幂乘,矩阵乘法,最邻近点对,多项式求值 |
代码
例1:阶乘函数
int facorial(int n)
{
if(n==0) return 1;
return n*facorial(n-1);
例2:fibonacci数列
int fibonacci(int n)
{
if(n<=1) return 1;
return fibonacci(n-1)+fibonacci(n-2);
}
例3:排列问题
template<class Type>
void Perm(Type list[],int k,int m)
{
//产生list[k,m]的所有排列
if(k==m)
{
//只剩下一个元素
for(int i-0;i<=m;i++)cout<<list[i];
cout<<endl;
}
else//将list[k:m]中的每个元素分别与list[k]中的元素进行交换,
然后递归计算list[k+1:m]的全排列
{
for(int i=k;i<=m;i++)
{
Swap(list[k],list[i]);
Perm(list,k+1,m);
Swap(list[k],list[i]);
}
}
template<class Type>
{
inline void Swap(Type &a,Type &b)
{
Type temp=a;
a=b;
b=temp;
}
}
例4:整数划分问题
int q(int n,int m)//n为要划分的整数,将最大加数n1<=m的划分个数记做q(n,m)
{
if((n<1)||(m<1))return 0;
if((n==1)||(m==1))return 1;
//最大加数不超过1
if(n<m) return q(n,n);//最大加数大于n
if(n==m)return q(n,m-1)+1;//q(n,n)=q(n,n-1)+1;1是n1=n的划分个数
return q(n,m-1)+q(n-m,m)//假设分解6(n)的最大加数不超过3(m),这个划分个数=最大加数不超过2的划分个数(因为可能会有最大加数为1的,所以不能用n-m作为第一个参数)+最大加数是3的划分个数(把3再划分为最大加数不超过6-3=3的划分个数,即3个)
}
例5:Hanoi塔问题
void hanoi(int n,int a,int b,int c)//n是圆盘个数,a,b,c表示塔座
{
//把塔座a的圆盘移动到塔座b上,c为辅助塔座
if(n>0)
{
hanoi(n-1,a,c,b);//把塔座a的n-1个圆盘移动到塔座c上,以塔座b为辅助塔座
mov(a,b);//将a上编号为n的圆盘移到b上
hanoi(n-1,c,b,a);//将塔座c的n-1个圆盘移动到塔座b上,以塔座a为辅助塔座
}
}
分治法的基本思想
divide and conquer(P)
{
if(|P|<=n0)adhoc(P);问题P的规模不超过n0时,直接解小规模问题P
divide P into smaller subinstances P1,P2...Pk
for(i=1;i<=k;i++)
yi=divide and conquer(Pi);
return merge(y1,y2,..yk);//归并解
}
例6:二分搜索算法
template <class Type>
int BinarySearch(Type a[],const Type &x,int n)
{
//在a[0]-a[n-1]中找x
int left=0;int right=n-1;
while(left<=right)
{
int middle=(left+right)/2;
if(x==a[middle])return middle;
if(x>a[middle]) left=middle+1;
else right=middle-1;
}
return -1;
例7:棋盘覆盖
void ChessBoard (int tr,int tc,int dr,int dc,int size)
{
//棋盘左上角的坐标(tr,tc),特殊方格的坐标(dr,dc),size=2^K,棋盘规格2^K*2^K
if(size==1) return;//棋盘规格为1*1
int t=tile++;//L型骨牌号
s=size/2;//分割棋盘
//覆盖左上角子棋盘
if(dr<tr+s&&dc<tc+s)//特殊方格在此棋盘中
ChessBoard(int tr,tc,dr,dc,s);
else//此棋盘无特殊方格
{
//用t号L型骨牌覆盖右下角
Board[tr+s-1][tc+s-1]=t;
//覆盖其他方格
ChessBoard(tr,tc,tr+s-1,tc+s-1,s);//以刚才L型骨牌覆盖的方格作为特殊方格进行继续覆盖
}
//覆盖右上角子棋盘
if(dr<tr+s&&dc>=tc+s)
//特殊方格在此棋盘中
ChessBoard(tr,tc+s,dr,dc,s);
else{
//此棋盘中无特殊方格
Board[tr+s-1][tc+s]=t;
ChessBoard(tr,tc+s,tr+s-1,tc+s,s);}
//覆盖左下角子棋盘
if(dr>=tr+s&&dc<tc+s)
ChessBoard(tr+s,tc,dr,dc,s);
else{
Board[tr+s][tc+s-1]=t;
ChessBoard(tr+s,tc,tr+s,tc+s-1,s);}
//覆盖右下角子棋盘
if(dr>=tr+s&&dc>=tc+s)
ChessBoard(tr+s,tc+s,dr,dc,s);
else{
Board[tr+s][tc+s]=t;
ChessBoard(tr+s,tc+s,tr+s,tc+s,s);
}
}
左上角
右上角棋盘
例8:合并排序
template<class Type>
void MergeSort(Type a[],int left,int right)
{
if(left<right){
//至少2个元素
int i=(left+right)/2;//取中点
MergeSort(a,left,i);
MergeSort(a,i+1;right);
Merge(a,b,left,i,right);