王道复试机试(6)

递归与分治
递归出口:可以有多个,但是至少要有一个
递归调用:我用我自己,调用整体必须往调用出口的方向靠近

  • 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

  1. 拐点 52431
  2. 交换 53421
  3. 排序 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; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值