分治与递归
1. 递归
两个核心
- 子问题必须与原始问题相同,且规模更小(递归式)
- 不能无限制地调用自身,必须有一个递归出口 (递归边界)
2.分治法
步骤
- 分:将问题分解为规模更小的问题(严格来说根据这一步的不同还可能分为减治法和分治法)
- 治:将这些规模更小的子问题逐个击破
- 合:将已解决的子问题合并,最终得出“母”问题的解
跟递归的关系
分治法既可以用递归求解也可以不用递归求解,但是一般来说都是用递归求解的,因为用递归代码写起来更加容易,因此我们将上面两种算法合到一起举例
3.举例
例子
- 汉诺塔1中移动次数计算(F(n) = 2 * F(n-1) + 1)
- 汉诺塔2(每次只能移动至相邻的塔)中移动次数计算(F(n) = 3 * F(n-1) + 2)
- 全排列问题
链接:https://www.nowcoder.com/questionTerminal/5632c23d0d654aecbc9315d1720421c1?f=discussion
来源:牛客网
//算法思想:全排列的n皇后问题思路,对问题进行递归实现。
//只不过将全排列的数字换为字母进行输出即可。
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=7;//P为当前排列,hashTable记录x是否已经在p中
int n,hashTable[maxn]={false};//当前处理排列的index位
char p[maxn];//储存全排列结果的数组
void generateP(int index,char str[])
{
if(index==n)//递归边界
{
for (int i = 0; i <n; ++i)
{
printf("%c", p[i]);//输出当前排列
}
printf("\n");
return;
}
for (int x = 0; x <n; ++x)//枚举1~n,试图将x填入p【index】
{
if (hashTable[x]==false)//如果x不在p[0]~p[index-1]中
{
p[index]=str[x];//令p的index位为先,即把str[x]加入当前排列
hashTable[x]=true;//记x已经在p中
generateP(index+1,str);//处理排列的index+1位
hashTable[x]=false;//已处理完p[index]为x的子问题,还原状态
}
}
}
int main(int argc, char const *argv[])
{
char str[maxn];
while(scanf("%s",str)!=EOF)
{
n=strlen(str);
sort(str,str+n);//将原始数组进行大小排序
generateP(0,str);
printf("\n");
}
return 0;
}
- n皇后问题(由上面的全排列问题改编而来,实际上还可以进一步通过回溯法进行剪枝,具体见算法笔记P117)
void generateP(int index)
{
if (index == n + 1) //递归边界,生成一个排列
{
bool flag = true; //flag为true表示当前排列为一个合法方案,
for (int i = 1; i <= n; i++) //遍历任意两个皇后
{
for (int j = i + 1; j <= n; j++)
{
if (abs(i - j) == abs(P[i] - P[j])) //如果x方向和y方向的距离相等说明在一条对角线上
{
flag = false;
}
}
if (flag == true)
{
count++;
}
return;
}
for (int x = 1; x <= n; x++)
{
if (hashTable[x] == false)
{
P[index] = x;
hashTable[x] = true;
generateP(index + 1);
hashTable[x] = false;
}
}
}