递归算法求解遍历(或穷举)问题

递归问题可以理解为遍历问题,必须遍历出所有的数据来,才能进行相应的运算,比如Fibonacci问题、阶乘问题,必须把每一步的值都遍历出来,然后才能做加法或乘法。

递归算法解决问题的特点:

(1)必须有一个明确的递归结束条件,称为递归出口。

(2)根据当前状态的值推断下一个状态值的个数n与条件,本次递归调用将根据条件调用n个自身(根据条件,遍历不同分支,如二叉树中前序、中序和后序遍历)。

递归算法优缺点:借助递归方法,我们可以把一个相对复杂的问题转化为一个与原问题相似的规模较小的问题来求解,递归方法只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。但在带来便捷的同时,也会有一些缺点,也即:通常用递归方法的运行效率不高。

使用递归算法处理的几类问题

1.Fibonacci问题

讲到递归,我们最先接触到的一个实例便是斐波那契数列。

斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,…

特别指出:第0项是0,第1项是第一个1。

这个数列从第二项开始,每一项都等于前两项之和。

斐波那契数列递归法实现:

int Fib(int n)

{

if(n<1)         /*结束条件*/

{

return -1;

}

if(n == 1|| n == 2)  /*结束条件*/

{

return 1;

}

return Fib(n-1)+Fib(n-2);  //很庆幸,Fibonacci有明显的递归方程

}

2.二叉树遍历问题

二叉树的遍历分为三种:前(先)序、中序、后序遍历。

设L、D、R分别表示二叉树的左子树、根结点和遍历右子树,则先(根)序遍历二叉树的顺序是DLR,中(根)序遍历二叉树的顺序是LDR,后(根)序遍历二叉树的顺

序是LRD。还有按层遍历二叉树。这些方法的时间复杂度都是O(n),n为结点个数。

假设我们有一个包含值的value和指向两个子结点的left和right的树结点结构。我们可以写出这样的过程:

先序遍历(递归实现):

visit(node)

{

print node.value    /*当前状态有两个可能分支*/

if node.left  != null then visit(node.left)

if node.right != null then visit(node.right)

return

}

中序遍历(递归实现):

visit(node)

{

if node.left  != null then visit(node.left)

print node.value

if node.right != null then visit(node.right)

return

}

后序遍历(递归实现):

visit(node)

{

if node.left  != null then visit(node.left)

if node.right != null then visit(node.right)

print node.value

return

}

3.字符串全排序或条件全遍历问题

问题:写一个函数返回一个串的所有排列。

解析:对于一个长度为n的串,它的全排列共有A(n, n)=n!种。这个问题也是一个递归的问题, 不过我们可以用不同的思路去理解它。为了方便讲解,假设我们要考察的串是”abc”, 递归函数名叫permu。

思路一:我们可以把串“abc”中的第0个字符a取出来,然后递归调用permu计算剩余的串“bc” 的排列,得到{bc, cb}。然后再将字符a插入这两个串中的任何一个空位(插空法), 得到最终所有的排列。比如,a插入串bc的所有(3个)空位,得到{abc,bac,bca}。 递归的终止条件是什么呢?当一个串为空,就无法再取出其中的第0个字符了, 所以此时返回一个空的排列。代码如下:

typedef vector vs;

vs permu(string s){              /*返回当前次排序的结果VS*/

vs result;

if(s == ""){                    /*退出条件*/

result.push_back("");

return result;

}

string c = s.substr(0, 1);

vs res = permu(s.substr(1));     /*与上一次排序结果的关系*/

for(int i=0; i

string t = res[i];

for(int j=0; j<=t.length(); ++j){

string u = t;

u.insert(j, c);

result.push_back(u);

}

}

return result; //调用result的拷贝构造函数,返回它的一份copy,然后这个局部变量销毁(与基本类型一样)

}

思路二:我们还可以用另一种思路来递归解这个问题。还是针对串“abc”, 我依次取出这个串中的每个字符,然后调用permu去计算剩余串的排列。 然后只需要把取出的字符加到剩余串排列的每个字符前即可。对于这个例子, 程序先取出a,然后计算剩余串的排列得到{bc,cb},然后把a加到它们的前面,得到{abc,acb};接着取出b,计算剩余串的排列得到{ac,ca},然后把b加到它们前面, 得到{bac,bca};后面的同理。最后就可以得到“abc”的全序列。

vs permu1(string s){

vs result;

if(s == ""){

result.push_back("");

return result;

}

for(int i=0; i

string c = s.substr(i, 1);

string t = s;

vs res = permu1(t.erase(i, 1));

for(int j=0; j

result.push_back(c + res[j]);

}

}

return result;

}

4.汉罗塔问题

汉诺塔是根据一个传说形成的数学问题:有三根杆子A,B,C。A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至C杆:每次只能移动一个圆盘;大盘不能叠在小盘上面。提示:可将圆盘临时置于B杆,也可将从A杆移出的圆盘重新移回A杆,但都必须遵循上述两条规则。

问:如何移?最少要移动多少次?

#include

#include

using namespace std;

void hannoi (int n, char from, char buffer, char to)

{    if (n == 1)

{        cout <

}

else

{

hannoi (n-1, from, to, buffer);

cout <

hannoi (n-1, buffer, from, to);    }}

int main(){

int n;    cin >> n;

hannoi (n, 'A', 'B', 'C');

return 0;

}

5.八皇后问题

问题:

经典的八皇后问题,即在一个8*8的棋盘上放8个皇后,使得这8个皇后无法互相***(任意2个皇后不能处于同一行,同一列或是对角线上),输出所有可能的摆放情况。

解析:

8皇后是个经典的问题,如果使用暴力法,每个格子都去考虑放皇后与否,一共有264种可能。所以暴力法并不是个好办法。由于皇后们是不能放在同一行的, 所以我们可以去掉“行”这个因素,即我第1次考虑把皇后放在第1行的某个位置, 第2次放的时候就不用去放在第一行了,因为这样放皇后间是可以互相***的。 第2次我就考虑把皇后放在第2行的某个位置,第3次我考虑把皇后放在第3行的某个位置, 这样依次去递归。每计算1行,递归一次,每次递归里面考虑8列, 即对每一行皇后有8个可能的位置可以放。找到一个与前面行的皇后都不会互相***的位置, 然后再递归进入下一行。找到一组可行解即可输出,然后程序回溯去找下一组可靠解。

我们用一个一维数组来表示相应行对应的列,比如c[i]=j表示, 第i行的皇后放在第j列。如果当前行是r,皇后放在哪一列呢?c[r]列。 一共有8列,所以我们要让c[r]依次取第0列,第1列,第2列……一直到第7列, 每取一次我们就去考虑,皇后放的位置会不会和前面已经放了的皇后有冲突。 怎样是有冲突呢?同行,同列,对角线。由于已经不会同行了,所以不用考虑这一点。 同列:c[r]==c[j];同对角线有两种可能,即主对角线方向和副对角线方向。 主对角线方向满足,行之差等于列之差:r-j==c[r]-c[j];副对角线方向满足, 行之差等于列之差的相反数:r-j==c[j]-c[r]。 只有满足了当前皇后和前面所有的皇后都不会互相***的时候,才能进入下一级递归。

#include

using namespace std;

int c[20], n=8, cnt=0;

void print()

{

for(int i=0; i

{

for(int j=0; j

{

if(j == c[i])

cout<

else cout<

}

cout<

}

cout<

}

void search(int r)

{

if(r == n)

{

print();

++cnt;

return;

}

for(int i=0; i

{

c[r] = i;

int ok = 1;

for(int j=0; j

if(c[r]==c[j] || r-j==c[r]-c[j] || r-j==c[j]-c[r])

{

ok = 0;

break;

}

if(ok) search(r+1);

}

}

int main()

{

search(0);

cout<

return 0;

}

在实际应用中,碰到最多的是第1,2,3类遍历问题,如目录下所有文件的遍历类似于二叉树问题,阶乘问题类似于Fibonacci问题。

下面是一个条件全遍历的例子:

给定一个n,写出n对‘()’的全部正确组合。

例如,给定n=3,输出结果为:

"((()))", "(()())", "(())()", "()(())", "()()()"

采用递归树的思想,遍历条件:

当左括号数大于右括号数时可以加左或者右括号;否则只能加左括号;当左括号数达到n时,剩下全部加右括号

在该方法中采用的正向遍历。

class Solution

{

public:

vector generateParenthesis(int n)

{

vector res;

generate(res, "", 0, 0, n);

return res;

}

void generate(vector res, string tmp, int lhs, int rhs, int n)  /*当前状态*/

{

if(lhs == n)

{

for(int i = 0; i

{

tmp += ")";

}

res.add(tmp);

return ;

}

if(lhs > rhs)                    /*遍历分支:经典之处*/

{

/*先遍历当前状态后,下一个状态是’(’的所有状态*/

generate(res, tmp + "(", lhs + 1, rhs, n); /*下一个可能状态1*/

/*然后遍历当前状态后,下一个状态是’)’的所有状态*/

generate(res, tmp + ")", lhs, rhs + 1, n); /*下一个可能状态2*/

}

else

{

generate(res, tmp + "(", lhs + 1, rhs, n); /*下一个可能状态3*/

}

}

}

当前状态能推断下一个状态,或由下一个状态能推断当前状态的情况,都适合递归。与状态有关的值需要当做入参或返回值传递。

java获取数组穷举_递归算法求解遍历(或穷举)问题相关推荐

  1. java获取数组最大最小值

    使用java获取数组最大最小值,可以采用两种方法.一种是对数组排序,排序后的数组第一位和最后一位即为最小值和最大值.另外一种方法是假定第一个数为最大或最小值,与其余数依次比较,遍历数组后即可得到最大或 ...

  2. java 字符数组与字符串_用于字符串和数组的5种简单有效的Java技术

    java 字符数组与字符串 Java通常会排在前五种最流行的编程语言中,因此可以理解. 它是一种通用语言,非常容易学习,因为它是一种高级语言,并且可以满足许多用例. 由于这些原因,Java是一门很棒的 ...

  3. java获取数组的最小值_Java 数组获取最大和最小值的实例实现

    以下实例演示了如何通过 Collections 类的 Collections.max() 和 Collections.min() 方法来查找数组中的最大和最小值: Main.java 文件: impo ...

  4. scala 获取数组中元素_从Scala中的元素列表中获取随机元素

    scala 获取数组中元素 We can access a random element from a list in Scala using the random variable. To use ...

  5. java获取vdx文件数据_通过文件名获取文件类型ContentType

    第一种方法: 引用System.Web.dll v4.5,就可以通过var mimeType = MimeMapping.GetMimeMapping(fileName);直接获取文件类型 第二种方法 ...

  6. java获取vm运行参数_如何获取在Java中运行Java VM的版本?

    java获取vm运行参数 To get the version of running VM (Virtual Machine) in Java, we use the getProperties() ...

  7. java对数组进行排序_用Java对数组进行排序所需的最少交换

    java对数组进行排序 Problem: 问题: In this problem, we would have an unordered array with consecutive distinct ...

  8. java 获取文件扩展名_如何在Java中获取文件扩展名

    java 获取文件扩展名 Sometimes while working with files, we need to process them differently based on their ...

  9. java动态数组的实现_动手编写—动态数组(Java实现)

    目录 数组基础回顾 自定义动态数组 动态数组的设计 抽象父类接口设计 抽象父类设计 动态数组之DynamicArray 补充数组缩容 全局的关系图 声明 数组基础回顾 1.数组是一种常见的数据结构,用 ...

最新文章

  1. 微信公众号H5订阅消息开发 uniapp订阅消息
  2. java获取mysql表结构
  3. 【Python】又一个Jupyter神器,操作Excel自动生成Python代码!
  4. IDEA Tomcat Catalina Log出现乱码
  5. VS中PCL库附加依赖项配置
  6. java jar killed_我的Java应用程序被OOMKilled了,原因竟是这样?
  7. VS2012项目中使用CocoStudio相关文件的设置
  8. (转)基于Ajax的应用程序架构汇总
  9. 蓝桥杯 ALGO-45算法训练 调和数列问题
  10. python3读取键盘输入_Python读取键盘输入
  11. 2010计算机系助学金,计算机系贫困生助学金申请书范文
  12. 微信多客服系统开发教程
  13. 给初学者推荐的10个Python免费学习网站,赶紧收藏吧
  14. matlab标定工具箱 使用,关于 matlab 标定工具箱的使用 | 学步园
  15. 2022-2028全球与中国犁螺栓市场现状及未来发展趋势
  16. 【web前端初级课程】第八章 什么是事件?
  17. 数据仓库架构的技术选型
  18. No converter found for return value of type错误解决以及消息转化器简单分析
  19. 得之坦然,失之淡然,顺其自然,争其必然。真的太经典了啊!
  20. 敏捷开发 宣言 思想 认识误区

热门文章

  1. 与 HarmonyOS 拼速度?谷歌正式推出 Fuchsia OS!
  2. 年轻人,看看 Redisson 分布式锁—可重入锁吧!太重要了
  3. 聊聊下一代监控:Prometheus
  4. OpenCV AI Kit (OAK) 创始人Brandon Gilles访谈全记录
  5. 漫画:大厂总体 SaaS 化部署,到底什么是 IaaS、PaaS 和 SaaS?
  6. 春招妥了!资深技术面试官教你这样准备 Java 面试! | CSDN 博文精选
  7. 为什么说阿里巴巴已进化成为一家世界级的科技公司?
  8. “一百万行 Python 代码对任何人都足够了”!
  9. 2020 年软件开发趋势预测!
  10. 小米主办HBaseCon亚洲峰会,打造世界一流的“工程师理想乐园”