第二章 递归与分治策略
1. 递归
阶乘函数(O(n))
int factorial(int n)
{
if(n==0) return 1;
return n*factorial(n-1);
}
Fibonacci数列(O(2^n))
int fibonacci(int n)
{
if(n<=1) return 1;
return fibonacci(n-1)+fibonacci(n-2);
}
排列问题
-
问题描述:对n个元素进行全排列,列出所有情况,例如1,2,3三个数字会得到1 2 3,1 3 2,2 1 3,2 3 1,3 1 2,3 2 1这6中情况
-
思路:设n为元素个数,元素集合为R(r1,r2,r3…rn),计算方法为Perm(n)
当n = 1时,则只有一种情况 r;
当n > 1时,则有perm(R)由(r1)Perm(R1),(r2)Perm(R2),(r3)Perm(R3) … … (rn)Perm(Rn)
-
算法思想:在一般情况下,k<m。算法将list[k:m]中的每个元素分别与list[k]中的元素交换,然后递归地计算list[k+1:m]的全排列,并将计算结果作为list[0:k]的后缀。
-
代码
#include<iostream>
using namespace std;
void perm(int list[], int k, int 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]); //还原
}
}
}
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int a[3] = {1, 2, 3};
perm(a, 0, 2);
return 0;
}
Hanoi塔问题
- 问题描述
- 算法思想(递归)
(1)当n=1
时,问题比较简单。此时,只要将编号为1的圆盘从塔座a直接移至塔座b上即可。
(2)当n>1
时,需要利用塔座c作为辅助塔座。此时要设法将n-1个较小的圆盘依照移动规则从塔座a移至塔座c上,然后将剩下的最大圆盘从塔座a移至塔座b上,最后设法将n-1个较小的圆盘依照移动规则从塔座c移至塔座b上。
(3)由此可见,n个圆盘的移动问题就可分解为两次n-1个圆盘的移动问题,这又可以递归地用上述方法来做。 - 代码
void hanoi( int n,int a,int b,int c)
//hanoi(n, a,b, c)表示将塔座a上自下而上,由大到小叠放在一起的n个圆盘依移动规则移至塔座b上并仍按同样顺序叠放。
//在移动过程中,以塔座c作为辅助塔座。
{
if(n>0)
{
hanoi(n-1,a,c,b); //以b为辅助,把a移到c
move(a,b); //move(a, b)表示将塔座a上编号为n的圆盘移至塔座b上
hanoi(n-1,c,b,a) //以a为辅助,把c移到b
}
}
- 时间复杂度分析:求解如下递归关系得
O(2^n)
2.分治法
- 基本思想
将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。递归地解这些子问题,然后将各子问题地解合并得到原问题的解。 - 分治法复杂性分析
(1)递归树法
(2)主定理法
二分搜索技术(O(logn))
- 基本思想
将n个元素分成个数大致相同的两半,取a[n/2]与x作比较。
如果x=a[n/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)
{
int right=n-1;
int left=0;
while(left<=right)
{
int middle=(left+right)/2;
if(x==a[middle]) return middle;
else if(x>a[middle]) left=middle+1;
else right=middle-1;
}
return -1; //没有找到x
}