递归与分治
递归出口:可以有多个,但是至少要有一个
递归调用:我用我自己,调用整体必须往调用出口的方向靠近
- N的阶乘
- 汉诺塔问题
F(n)=2 * F(n-1) + 1 ;F(1)=1;
–>an + 1 = 2*an-1 + 2 = 2(an-1 + 1)
–>bn = an + 1
–>bn = 2bn-1 b1 = a1+1 = 2 bn为q=2的等比数列
–>bn = b1 * q^n-1 = 2 * 2^n-1 = 2^n
–>an + 1 = 2^n
–>an = 2^n -1
long long hanoi(int n)
{
if(n==1)
return 1;
else
return 2*hanoi(n-1)+1;
}
-
汉诺塔III HDU2064(必须要经过相邻的柱子 左->中->右,不能直接左->右)
1.n-1(左)-->n-1(中)-->n-1(右) 步数F(n-1),因为定义F(n)即为左->右 2.1(左)->1(中) 步数1 3.n-1(左)<--n-1(中)<--n-1(右) 步数F(n-1) 4.1(中)-->1(右) 步数1 5.n-1(左)-->n-1(中)-->n-1(右) 步数F(n-1)
F(1)=2
F(n) = 3F(n-1)+2
–>an = 3 * an-1 + 2
–>an + 1 = 3 * an-1 + 3
–>bn = 3 * bn-1 即公比q = 3 b1 = 2 + 1 = 3
–>bn = 3 * b1 * q^n-2 = 3 * 3 * 3^n-2 = 3^n
–>an = 3^n - 1
long long hanoi(int n)
{
if(n==1)
return 2;
else
return 3*hanoi(n-1)+2;
}
- 全排列
①递归方法
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
using namespace std;
const int MAXN =10;
bool visit[MAXN]; //标记访问过的字母
char sequence[MAXN]; // 记录生成的字符串
void Get(string str,int index)
{
if(index==str.size()) // 递归出口,已经遍历到了最底层
{
for(int i=0 ; i< str.size() ; ++i)
{
printf("%c",sequence[i]); // 输出遍历好的一个数列
}
printf("\n");
}
for(int i=0;i<str.size();++i) // 在某一层某一个字符被用过
{
if(visit[i]==true) //比如str[0]已经访问了为了不重复出现则跳入下一个str[1]
continue;
visit[i]=true; //还没用过,用后标记为已使用过,下一层就不能再用了
sequence[index]=str[i];
Get(str,index+1); // 往下面层继续访问,所以index要+1
visit[i]=false; //比如已经访问ac,第三次要访问b,但是b已经在之前访问过,若还为true则跳过,所以要把访问标记删除
}
}
int main()
{
string str;
while(cin>>str)
{
sort(str.begin(),str.end());
Get(str,0);
cout<<endl;
}
return 0;
}
②非递归方法
思想:假设要将12345数字进行全排列,全排列中的数字最大为54321
若我排列到了52431,那么下一个是53124
- 拐点 52431
- 交换 53421
- 排序 53124
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
using namespace std;
string str;
bool GetNext() //bool标记是否到了最后的全排列
{
int n= str.size();
int index = n-1; // 用index来寻找拐点,拐点就是从右往左找一个有下降趋势的位置
while(index>=1&&str[index]<str[index-1])
{
index--; //此时这里的index表示的是拐点,也就是举例中的4的位置
}
index--;// inde放置在有下降趋势的,拐点的前面那个位置,也就是举例中2的位置
if(index<0) //说明已经找到了最后的那个即-1位置,12345则为此情况,为最后情况
return false;
for(int i = n-1 ; i > index ; --i) // 交换即52431中2,3位置互换
{
if(str[i]>str[index])
{
swap(str[i],str[index]);
break;// 只交换一次
}
}
reverse(str.begin()+index+1,str.end()); // 从拐点开始的地方逆序,而不是从index的位置开始逆序
return true;
}
int main()
{
while(cin>>str)
{
sort(str.begin(),str.end());
do
{
cout<<str<<endl;
}while(GetNext());
cout<<endl;
}
return 0;
}
③系统函数
next_permutation()
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
using namespace std;
int main()
{
string str;
while(cin>>str)
{
sort(str.begin(),str.end());
do
{
cout<<str<<endl;
}while(next_permutation(str.begin(),str.end()));
cout<<endl;
}
return 0;
}
分治法
- 斐波那契数
①分治法O(2^n) 直接用递归来求某位置上的斐波那契数
②递推法O(n) 实则线性初始化一个数组,存储中间值
③矩阵快速幂O(logn)
//分治法
int Fibonacci(int n)
{
if(n==1||n==0)
return n;
else
{
return Fibonacci(n-1)+Fibonacci(n-2);
}
}
//递推法
#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN=35;
int fibonacci[MAXN];
void initial()
{
fibonacci[0]=0;
fibonacci[1]=1;
for(int i=2 ;i<MAXN ;i++)
{
fibonacci[i]= fibonacci[i-1]+fibonacci[i-2];
}
}
int main()
{
initial();
int n;
while(scanf("%d",&n)!=EOF)
{
printf("%d\n",fibonacci[n]);
}
return 0;
}
懒得写矩阵乘法和快速幂了。。。
//快速幂
int main()
{
while(scanf("%d",&n)!=EOF)
{
Matrix fibonacci = Matrix(2,2);
fibonacci.matrix[0][0]=1;
fibonacci.matrix[0][1]=1;
fibonacci.matrix[1][0]=1;
fibonacci.matrix[1][1]=0;
Matrix answer= QuickPower(fibnacci,n);
printf("%d\n",answer.matrix[1][0]);
}
return 0;
}
- 二叉树
n结点的m结点的字数有多少个结点
F(m) = F(2m) + F(2m+1)+1
int countnode(int m,int n)
{
if(m>n)
return 0;
else
return count(2*m,n)+count(2*m+1,n)+1;
}