一、递归分治
这里的很多资料,都是我从网上看别人的博客,或者从书上摘录下来的,如有侵权,请联系我,立马删帖
1.1 分治法
分治法(Divid-and-Conquer):将问题划分为n个规模较小而结构与原问题相似的子问题;递归地解决这些子问题,然后再合并其结果,就得到原问题地解。
分治模式在每一层递归上都有三个步骤:
- 分解(Divide):将原问题解成一系列子问题;
- 解决(Conquer):递归地解决各个子问题。若子问题足够小,则直接求解。
- 合并(Combine):将子问题的结果合并成原问题的解。
使用条件:
- 该问题的规模缩小到一定的程度就可以容易地解决
- 该问题可以分解为若干规模较小的相同问题
- 利用该问题分解处的子问题的解可以合并为该问题的解
- 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。
注:递归算法是一种自底向上逐步求出原来问题的解
1.2代码演示
1.2.1递归
[例题1] 阶层函数递归表示:
int factorial(int n)
{
if(n==0)
return 1;
return n*factorial(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,nt m) // 产生list[k:m]的所有排列
{
if(k==m)
{
for(int i=0;i<=m;i++)
cout<<list[i];
cout<<endl;
}
else{ // 还有很多元素待排列,递归产生排列
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;
}
1.2.2 二分搜索技术
算法思想:将n个元素分成个数大致相同的两半,取a[n/2]与x作比较。如果x=[a/2],则找到x,算法终止;如果x<a[n/2],则只在数组a的左半部继续搜索x;如果x>a[n/2],则只在数组a的右半部分继续搜索x。具体算法描述如下:
template<class Type>
int BinarySearch(Type a[],const Type& x,int n) // 在a[0]<=a[1]<=...<=a[n-1]中搜索x
// 找到x时返回其在数组中的位置,否则返回-1
{
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; // 未找到x
}
1.2.3 棋盘覆盖
void ChessBoard(int tr,int tc,int dr,int dc,int size)
{
if(size==1)
return;
int t=tile++; // L型骨牌号
s=size/2; // 1/4大小,size指2^k(分割棋盘)
// 覆盖左上角子棋盘
if(dr<tr+s && dc<tc+s) // 特殊方格在此棋盘中
ChessBoard(tr,tc,dr,dc,s); // 继续递归
else // 此棋盘中无特殊方格
{
Board[tr+s-1][tc+s-1]=t; // 用t号L型骨牌覆盖右下角
ChessBoard(tr,tc,tr+s-1,tc+s-1,s); // 覆盖其余方格
}
if(dr<tr+s && dc>=tc+s) // 覆盖右上角子棋盘
ChessBoard(tr,tc+s,dr,dc,s); // 特殊方格在此棋盘中
else // 此棋盘中无特殊方格
{
Board[tr+s-1][tc+s]=t; // 用t号L型骨牌覆盖左下角
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 // 用t号L型骨牌覆盖右上角
{
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 // 用t号L型骨牌覆盖左上角
{
Board[tr+s][tc+s]=t;
ChessBoard(tr+s,tc+s,tr+s,tc+s,s); // 覆盖其余方格
}
}
1.2.3 合并排序
基本思想:将待排序元素分成大小一致相同的两个子集合,分别对两个子集合进行排序,最终将排好序的子集合合并成要求的排好序的集合。
合并排序算法可递归地描述如下:
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,i);
Merge(a,b,left,i,right); // 合并到数组b
Copy(a,b,left,right); // 复制回数组a
}
}
消去递归后的合并排序算法可描述如下:
/*MergeSort()的递归过程只是将待排序集合一分为二,
直至待排序集合只剩下一个元素为止,然后不断合并两个排好序的数组段。
*/
template<class Type>
void MergeSort(Type a[],int n)
{
Type *b=new Type [n];
int s=1;
while(s<n)
{
MergePass(a,b,s,n); //合并到数组b
s += s;
MergePass(b,a,s,n); //合并到数组a
s+=s;
}
}
/*
函数MergePass()用于合并排好序的相邻数组段,具体的合并算法由Merge()函数来实现
*/
template<class Type>
void mergepass(Type x[],type y[],int s,int n) // 合并大小为s的相邻子数组
{
int i=0;
while(i<=n-2*s)
{
Merge(x,y,i,i+s-1,i+2*s-1); // 合并大小为s的相邻2段子数组
i=i+2*s;
}
if(i+s<n) // 剩下的元素个数少于2s
Merge(x,y,i,i+s-1,n-1);
else
for(int j=i;j<=n-1;j++)
y[j]=x[j];
}
template<class Type>
void Merge(Type c[],Type d[],int l,int m,int r) // 合并c[1:m]和c[m+1:r]到d[1:r]
{
int i=1,j=m+1,k=1;
while((i<=m) && (j<=r))
{
if(c[i]<=c[j])
d[k++]=c[i++];
else
d[k++]=c[j++];
if(i>m)
{
for(int q=j;q<=r;q++)
d[k++]=c[q];
}
else
{
for(int q=i;q<=m;q++)
d[k++]=c[q];
}
}
}
1.2.4 快速排序
template<class Type>
void QuickSort(Type a[],int p,int r)
{
if(p<r)
{
int q=Partition(a,p,r);
QuickSort(a,p,q-1); // 对左半段排序
QuickSort(a,q+1,r); // 对右半段排序
}
}
/*
对含有n个元素的数组a[0:n-1]进行快速排序只要调用QuickSort(a,0,n-1)即可
上述算法中的函数Partition()以一个确定的基准元素a[p]对子数组a[p:r]进行划分,它是快速排序算法的关键。
*/
template<class Type>
int Partition(Type a[],int p,int r)
{
int i=p,j=r+1;
Type x=a[p];
// 将小于x的元素交换到左边区域,将大于x的元素交换到右边区域
while(true)
{
while(a[++i]<x && i<r);
while(a[--j]>x);
if(i>=j)
break;
Swap(a[i],a[j]);
}
a[p]=a[j];
a[j]=x;
return j;
}