目录

1 问题描述

2 解决方案

2.1 问题化简

2.2 定位输出测试

2.3 回顾总结


1 问题描述

最近两天在思考如何使用蛮力法解决旅行商问题(此问题,说白了就是如何求解n个不同字母的所有不同排序的序列问题,即共有n!次不同排序)。

为此,我认真看了一篇出自CSDN上的博客文章,其中有一段核心代码就是在for循环里面添加一句递归调用语句,来实现n!次排序。因此,我对文章中的那段核心代码苦苦不得其解——其执行顺序究竟是咋样的呢?

附其简要代码:


public int count = 0;
public  void Arrange(int[] A,int start,int step,int n,int Max){
if(step == 2)
System.out.println("第"+(++count)+"次走完一圈");
if(count == Max)
System.out.println("已完成!!!");
else{
for(int i = start;i < n;i++){
swapArray(A,start,i);
Arrange(A,start+1,step+1,n,Max);
swapArray(A,i,start);
}
}
}

2 解决方案

2.1 问题化简

刚开始学习递归时,课本上都会给出使用递归实现斐波那契数问题(PS:f(1) = 1,f(2) = 1,f(3) = 2,f(4) = 3,...,f(n) = f(n-1) + f(n-2),求解f(n)),那我们就先来探讨使用递归法求解第n个斐波那契数的具体执行顺序:

先上代码:

package com.liuzhen.chapterThree;

public class Test {
public int Fibonacci(int n){
System.out.println("第"+n+"次递归,结果:");
if(n == 1 || n == 2)
return 1;
System.out.println("********");
return Fibonacci(n-1) + Fibonacci(n-2);
} public static void main(String[] args){
Test temp = new Test();
System.out.println("运行结果:"+temp.Fibonacci(4));
}
}

运行结果:

第4次递归,结果:
********
第3次递归,结果:
********
第2次递归,结果:
第1次递归,结果:
第2次递归,结果:
第4次递归,结果:
********
第3次递归,结果:
********
第2次递归,结果:
第1次递归,结果:
第2次递归,结果:
运行结果:3

此处是求解第4个斐波那契数,看到运行结果依次输出数字4,3,2,1,2,4,3,2,1,2。可以看到,输出的数字按照第一遍输出的样式重复输出了一次。

此处递归语句:return Fibonacci(n-1) + Fibonacci(n-2);

为此先按照语句分析如下:PS:经网上相关资料提示:递归算法的本质是先递归后回溯,从而求得最终结果。以下只是本人根据程序运行结果做出的推测分析,如有错误,欢迎各位圆友指正~)

(1)Fibonacci(4) = Fibonacci(3) + Fibonacci(2),此时会首先输出数字4;

(2)接着执行Fibonacci(3) = Fibonacci(2) + Fibonacci(1),此时会首先输出数字3;

(3)接着执行(2)中Fibonacci(2),递归结束,此时会首先输出数字2;

(4)接着执行(2)中Fibonacci(1),递归结束,此时会首先输出数字1;

(5)接着执行(1)中Fibonacci(2),递归结束,此时会首先输出数字2;

(6)上面(1)~(5)步中仅仅只完成了递归算法,并未完成求取Fibonacci(4)的具体值步骤,此时,正式开始回溯求取Fibonacci(4),即Fibonacci(4) = Fibonacci(3) + Fibonacci(2),此时输出数字4;

(7)回溯求取(6)中Fibonacci(3)= Fibonacci(2) + Fibonacci(1),此时输出数字3;

(8)回溯求取(7)中Fibonacci(2)值,其在(3)中已获得为1,此时输出数字2;

(9)回溯求取(7)中Fibonacci(1)值,其在(4)中已获得为1,此时输出数字1,此时求得(6)中Fibonacci(3) = 2;

(10)回溯求取(6)中Fibonacci(2)值,其在(5)中已获得为1,此时输出数字2,此时求得6)中Fibonacci(2) = 1;

(11)输出最终结果Fibonacci(4) = 2+1 =3。

上述执行步骤求取Fibonacci(4)简单示意图:

2.2 定位输出测试

由2.1中对于斐波那契数问题的执行顺序探讨让我明白了一条递归的实质——先递归后回溯。在本文的问题中,还要谨记一点——递归的执行顺序遵循栈的特性——先进后出

先看本文所述问题具体测试代码:

package com.liuzhen.chapterThree;

public class TravelingSalesman {

    public int count = 0;
/*
* start为开始进行排序的位置
* step为当前正在排序的位置
* n为需要排序的总位置数
* Max为n!值
*/
public void Arrange(int[] A,int start,int step,int n,int Max){
if(step == 2)
System.out.println("第"+(++count)+"次走完一圈");
if(count == Max)
System.out.println("已完成!!!");
else{
System.out.println("准备进入for循环");
for(int i = start;i < n;i++){
swapArray(A,start,i);
System.out.println("递归调用前:"+" start值为"+start+",i的值为"+i);
Arrange(A,start+1,step+1,n,Max);
System.out.println("递归调用后:"+" start值为"+start+",i的值为"+i);
swapArray(A,i,start);
}
}
} public void swapArray(int[] A,int p,int q){
int temp = A[p];
A[p] = A[q];
A[q] = temp;
} public static void main(String[] args){
int[] A = {0,1};
TravelingSalesman test = new TravelingSalesman();
test.Arrange(A,0,0,2,2);
}
}

运行结果:

准备进入for循环
递归调用前: start值为0,i的值为0
准备进入for循环
递归调用前: start值为1,i的值为1
第1次走完一圈
准备进入for循环
递归调用后: start值为1,i的值为1
递归调用后: start值为0,i的值为0
递归调用前: start值为0,i的值为1
准备进入for循环
递归调用前: start值为1,i的值为1
第2次走完一圈
已完成!!!
递归调用后: start值为1,i的值为1
递归调用后: start值为0,i的值为1

此处是求取数字0和1的不同排序,即有2!= 2种不同排序,此处依照代码及运行结果来分析其具体执行顺序:

(1)

准备进入for循环:

递归调用前: start值为0,i的值为0(程序初始化值start为0,i等于start值为0)

(2)

准备进入for循环:

递归调用前: start值为1,i的值为1(执行完一次递归后,start值变为1,i等于start值也为1)

(3)

第1次走完一圈(输出此句,表示已经执行完两次递归,start值变为了2)

准备进入for循环:(此时,start值为2,即准备开始回溯,执行未完成的for循环)

递归调用后: start值为1,i的值为1(此处,对照栈的特性,开始进行(2)中未完成的for循环,输出此句后,将结束(2)中的for循环)

递归调用后: start值为0,i的值为0(此处,对照栈的特性,开始进行(1)中未完成的for循环,i值将要加1)

递归调用前: start值为0,i的值为1(此处,进行了(1)中一次for循环后,就要开始执行一次递归语句,start值将要加1)

(4)

准备进入for循环:

递归调用前: start值为1,i的值为1(此处,是执行(3)中递归语句的执行输出结果,此时也要开始执行一次递归语句,start值将要加1)

(5)

第2次走完一圈(此时,start值有变成了2)

已完成!!!

递归调用后: start值为1,i的值为1(此处,执行步骤(4)中未完成for循环的回溯,进行for循环)

递归调用后: start值为0,i的值为1(此处,执行步骤(3)中未完成for循环的回溯,进行for循环)

上述是对2!次不同排序的递归执行顺序分析,下面我们可以来看看对于3!次不同排序的递归执行顺序结果,看看其执行顺序是否满足栈——先进后出的特性,

PS:此处对上述代码中定义数字进行稍微修改,修改如下:

if(step == 3)
System.out.println("第"+(++count)+"次走完一圈"); public static void main(String[] args){
int[] A = {0,1,2};
TravelingSalesman test = new TravelingSalesman();
test.Arrange(A,0,0,3,6);
}

具体运行结果:

准备进入for循环
递归调用前: start值为0,i的值为0
准备进入for循环
递归调用前: start值为1,i的值为1
准备进入for循环
递归调用前: start值为2,i的值为2
第1次走完一圈
准备进入for循环
递归调用后: start值为2,i的值为2
递归调用后: start值为1,i的值为1
递归调用前: start值为1,i的值为2(此处即将执行一次递归)
准备进入for循环
递归调用前: start值为2,i的值为2
第2次走完一圈
准备进入for循环
递归调用后: start值为2,i的值为2
递归调用后: start值为1,i的值为2
递归调用后: start值为0,i的值为0
递归调用前: start值为0,i的值为1
准备进入for循环
递归调用前: start值为1,i的值为1
准备进入for循环
递归调用前: start值为2,i的值为2
第3次走完一圈
准备进入for循环
递归调用后: start值为2,i的值为2
递归调用后: start值为1,i的值为1
递归调用前: start值为1,i的值为2
准备进入for循环
递归调用前: start值为2,i的值为2
第4次走完一圈
准备进入for循环
递归调用后: start值为2,i的值为2
递归调用后: start值为1,i的值为2
递归调用后: start值为0,i的值为1
递归调用前: start值为0,i的值为2
准备进入for循环
递归调用前: start值为1,i的值为1
准备进入for循环
递归调用前: start值为2,i的值为2
第5次走完一圈
准备进入for循环
递归调用后: start值为2,i的值为2
递归调用后: start值为1,i的值为1
递归调用前: start值为1,i的值为2
准备进入for循环
递归调用前: start值为2,i的值为2
第6次走完一圈
已完成!!!
递归调用后: start值为2,i的值为2
递归调用后: start值为1,i的值为2
递归调用后: start值为0,i的值为2

可以看出,运行结果明显满足上述中2!次运行结果的分析及推测结论。

2.3 回顾总结

通过以上的分析及上机输出测试,可以初步得出递归算法执行顺序满足如下两点:

(1)先执行递归,后进行回溯;

(2)执行顺序满足栈的特性——先进后出。

算法笔记_017:递归执行顺序的探讨(Java)的更多相关文章

  1. MySQL笔记-语句的执行顺序

    在一次查询线上问题时发现有以下两条同样的SQL,执行后数据的顺序不一样: SELECT * FROM nns_assists_item AS asset WHERE asset.nns_assist_ ...

  2. SQLSERVER 2008 技术内幕 T-SQL查询 笔记1: SQL 执行顺序

    与大多数语言一样,SQL语言也有一个执行顺序,只是在大多数编程语言中,代码是按照编写顺序来处理的,而在SQL中则不是,下图为SQL 执行顺序. () ) [ ALL | DISTINCT ] () [ ...

  3. 算法笔记_052:蓝桥杯练习Multithreading(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 现有如下一个算法: repeat ni times yi := y y := yi+1 end repeat 令n[1]为你需要算加法的第 ...

  4. 算法笔记_013:汉诺塔问题(Java递归法和非递归法)

    目录 1 问题描述 2 解决方案  2.1 递归法 2.2 非递归法 1 问题描述 Simulate the movement of the Towers of Hanoi Puzzle; Bonus ...

  5. MVC学习笔记---MVC框架执行顺序

    一.把路由添加到路由表, 二.注册ControllerBuilder(老板)和默认工厂(DefaultControllerFactory) 2.1默认工厂获取可以创建的Controller. 三.由于 ...

  6. Junit4学习笔记--方法的执行顺序

    package com.lt.Demo.TestDemo; import java.util.Arrays; import java.util.Collection; import org.junit ...

  7. 算法笔记_044:表达式计算求值(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 输入一个只包含加减乖除和括号的合法表达式,求表达式的值.其中除表示整除. 输入格式 输入一行,包含一个表达式. 输出格式 输出这个表达式的 ...

  8. 算法笔记_189:历届试题 横向打印二叉树(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 二叉树可以用于排序.其原理很简单:对于一个排序二叉树添加新节点时,先与根节点比较,若小则交给左子树继续处理,否则交给右子树. 当遇到空子树 ...

  9. 算法笔记_185:历届试题 格子刷油漆(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 X国的一段古城墙的顶端可以看成 2*N个格子组成的矩形(如下图所示),现需要把这些格子刷上保护漆. 你可以从任意一个格子刷起,刷完一格,可 ...

随机推荐

  1. RunLoop基础

    序言 RunLoop一直是比较高级而又比较神秘的技术,一直以来都没有深入去阅读过苹果给出的官方文档.本篇文章就讲讲苹果官方文档中所介绍的RunLoop,再加上其开源性,让我们一起深入去研究其特性及与线 ...

  2. [Unity]Unity开发NGUI代码实现ScrollView(放大视图)

    Unity开发NGUI代码实现ScrollView(放大视图) 下载NGUI包 导入NGUI3.9.1版本package 创建MainCameraScript.cs脚本 MainCameraScrip ...

  3. iOS9网络适配

    今天升级Xcode7.0,发现网络访问失败. 输出错误信息:The resource could not be loaded because the App Transport Security po ...

  4. MUI开发注意事项

    mui开发注意事项,有需要的朋友可以参考下. mui是一个高性能的HTML5开发框架,从UI到效率,都在极力追求原生体验:这个框架自身有一些规则,刚接触的同学不很熟悉,特总结本文:想了解mui更详细的 ...

  5. MonkeyRecorder

    http://www.cnblogs.com/lynn-li/p/5894953.html

  6. IOS开发中各种型号的分辨率及软件图标的制作

    IOS中各手机的分辨率为: 5.5寸: 1242*2208;4.7寸: 750*1334;4.0寸: 640*1136;3.5寸: 640*960; 软件的图标有以下需求(注意选中右侧红色框中这一条) ...

  7. JS内存泄漏排查方法——Chrome Profiles

    一.概述 Google Chrome浏览器提供了非常强大的JS调试工具,Heap Profiling便是其中一个.Heap Profiling可以记录当前的堆内存(heap)快照,并生成对象的描述文件 ...

  8. PHP cookie禁用时session 方案

    在PHP中使用过SESSION的朋友可能会碰到这么一个问题,SESSION变量不能跨页传递.这令我苦恼了好些日子,最终通过查资料思考并解决了这个问题.我认为,出现这个问题的原因有以下几点: 1.客户端 ...

  9. MySQL服务器的线程数查看方法

    mysql重启命令:/etc/init.d/mysql restart MySQL服务器的线程数需要在一个合理的范围之内,这样才能保证MySQL服务器健康平稳地运行.Threads_created表示 ...

  10. HDU2066:一个人的旅行(Dijkstra)

    Problem Description 虽然草儿是个路痴(就是在杭电待了一年多,居然还会在校园里迷路的人,汗~),但是草儿仍然很喜欢旅行,因为在旅途中 会遇见很多人(白马王子,^0^),很多事,还能丰 ...