算法学习-类似汉诺塔的问题
2016-09-02 15:51
253 查看
题目:
汉诺塔问题比较经典,这里修改一下游戏规则:
现在限制不能从最左侧的塔直接移动到最右 侧,也不能从最右侧直接移动到最左侧,而是必须经过中间。求当塔有 N 层的时候,打印
最优移动过程和最优移动总步数。
例如,当塔数为两层时,最上层的塔记为 1,最下层的塔记为 2,则打印:
Move 1 from left to mid
Move 1 from mid to right
Move 2 from left to mid
Move 1 from right to mid
Move 1 from mid to left
Move 2 from mid to right
Move 1 from left to mid
Move 1 from mid to right
It will move 8 steps.
【要求】
用以下两种方法解决。
方法一:递归的方法;
方法二:非递归的方法,用栈来模拟汉诺塔的三个塔。
这个问题和经典的汉诺塔很像咯,汉诺塔是最经典的联系递归的算法了,这里左神用递归和非递归(栈)两种方式来实现了;
递归
上面就是递归操作的算法了,主要是分情况进行分析,这里要注意,很多人会问规则不是 只能从 mid 经过才能走吗,为什么还有类似于
这样(可能直接 left->right的代码呢),请注意,这是递归操作,我们只需要注意print的操作是完全符合 游戏规则就可以了,递归操作进入再分析的时候就会符合游戏规则啦~~
非递归操作(栈操作)
对于栈操作的时候,需要有几点说明:
首先,对于移动汉诺塔,符合要求的操作只有4种,LToM,MToL,MToR,RToM;
其次,由于要求的是最少步数,也就是如果前一步是LToM,则这步绝对不可能是MToL(这样不就循环操作了吗,怎么可能是最优步数);
最后,操作的规则,只能小压大;
对了,还有一个小的trick,为了使得初始化时都可以压入数据,事先在栈中压入最大值(不压入的话,需要处理当栈为空的情况);
鉴于上述规则,我们可以定义3个栈来进行操作,代码如下:
以上就是这个问题的求解过程了,比较能练手,好评~~~
汉诺塔问题比较经典,这里修改一下游戏规则:
现在限制不能从最左侧的塔直接移动到最右 侧,也不能从最右侧直接移动到最左侧,而是必须经过中间。求当塔有 N 层的时候,打印
最优移动过程和最优移动总步数。
例如,当塔数为两层时,最上层的塔记为 1,最下层的塔记为 2,则打印:
Move 1 from left to mid
Move 1 from mid to right
Move 2 from left to mid
Move 1 from right to mid
Move 1 from mid to left
Move 2 from mid to right
Move 1 from left to mid
Move 1 from mid to right
It will move 8 steps.
【要求】
用以下两种方法解决。
方法一:递归的方法;
方法二:非递归的方法,用栈来模拟汉诺塔的三个塔。
这个问题和经典的汉诺塔很像咯,汉诺塔是最经典的联系递归的算法了,这里左神用递归和非递归(栈)两种方式来实现了;
递归
// 递归做 public static int hanoiProblem1(int N,String left,String mid,String right){ if(N<1) return 0; return process1(N,left,mid,right,left,right); } // from —> to public static int process1(int N,String left,String mid,String right,String from,String to){ // 只有1层塔的情况 if(N==1){ // from/to存在mid的情况,直接一步就可完成from,to(游戏规则,只可以借助mid进行移动) if(from.equals(mid) || to.equals(mid)){ System.out.println("move 1 from "+from+" to "+to); return 1; }else{ // 移动不是在mid上进行,则需要借助mid进行两步操作 System.out.println("move 1 from "+from+" to mid"); System.out.println("move 1 from mid to "+to); return 2; } } // 其他情况(即 多层塔层叠) // 存在mid移动的情况,分三步 if(from.equals(mid) || to.equals(mid)){ // 3 steps // 现在要把塔从from->to上,因为是多层塔,所以需要借助另外的塔把压在上面的塔移走,这样最下面的塔才能移到to上啊~~~ String another=from.equals(left) || to.equals(left)?right:left; int part1=process1(N-1,left,mid,right,from,another); System.out.println("move "+N+" from "+from+" to "+to); int part2=process1(N-1,left,mid,right,another,to); return part1+part2+1; }else{ // 不是mid的情况,则需要借助mid进行5步操作了 // 5 steps int part1=process1(N-1,left,mid,right,from,to); System.out.println("move "+N+" from "+from+" to mid"); int part2=process1(N-1,left,mid,right,to,from); System.out.println("move "+N+" from mid to "+to); int part3=process1(N-1,left,mid,right,from,to); return part1+part2+part3+2; } }
上面就是递归操作的算法了,主要是分情况进行分析,这里要注意,很多人会问规则不是 只能从 mid 经过才能走吗,为什么还有类似于
int part1=process1(N-1,left,mid,right,from,another);
这样(可能直接 left->right的代码呢),请注意,这是递归操作,我们只需要注意print的操作是完全符合 游戏规则就可以了,递归操作进入再分析的时候就会符合游戏规则啦~~
非递归操作(栈操作)
对于栈操作的时候,需要有几点说明:
首先,对于移动汉诺塔,符合要求的操作只有4种,LToM,MToL,MToR,RToM;
其次,由于要求的是最少步数,也就是如果前一步是LToM,则这步绝对不可能是MToL(这样不就循环操作了吗,怎么可能是最优步数);
最后,操作的规则,只能小压大;
对了,还有一个小的trick,为了使得初始化时都可以压入数据,事先在栈中压入最大值(不压入的话,需要处理当栈为空的情况);
鉴于上述规则,我们可以定义3个栈来进行操作,代码如下:
public static enum Action { No, LToM, MToL, MToR, RToM } // 栈做 public static int hanoiProblem2(int N,String left,String mid,String right){ if(N<1) return 0; Stack<Integer> fStack=new Stack<Integer>(); Stack<Integer> mStack=new Stack<Integer>(); Stack<Integer> tStack=new Stack<Integer>(); fStack.push(Integer.MAX_VALUE); mStack.push(Integer.MAX_VALUE); tStack.push(Integer.MAX_VALUE); for(int i=N;i>=1;i--){ fStack.push(i); } // record主要是记录上一次的操作 Action[] record={Action.No}; int step=0; while(tStack.size()!=N+1){ step+=process2(record,Action.MToL,Action.LToM,fStack,mStack,left,mid); step+=process2(record,Action.LToM,Action.MToL,mStack,fStack,mid,left); step+=process2(record,Action.RToM,Action.MToR,mStack,tStack,mid,right); step+=process2(record,Action.MToR,Action.RToM,tStack,mStack,right,mid); } return step; } public static int process2(Action[] record,Action preNoAct,Action curAct,Stack<Integer> fStack, Stack<Integer> tStack,String from,String to){ // 当上一次的操作 为当前上次不允许的操作时,返回 // 当大压小时,不符合操作规则,返回 if(record[0]==preNoAct || fStack.peek()>=tStack.peek()){ return 0; } // 本次操作允许,记录 tStack.push(fStack.pop()); System.out.println("move "+tStack.peek()+" from "+from+" to "+to); record[0]=curAct; return 1; }
以上就是这个问题的求解过程了,比较能练手,好评~~~
相关文章推荐
- 算法学习之递归--汉诺塔问题
- 数据结构学习(2):汉诺塔问题
- 汉诺塔算法学习笔记
- 隐马尔可夫模型(七)——隐马尔可夫模型的学习问题(前向后向算法)
- 算法学习笔记----最大子数组问题
- 汉诺塔问题递归算法分析:
- 马牛的C#学习(第六天)-实践!用C#解决汉诺塔问题
- 学习图算法----最短路径问题
- 数据结构学习之递归求解汉诺塔问题
- 【算法学习】双调欧几里得旅行商问题(动态规划)
- Java数据结构和算法——汉诺塔问题
- 汉诺塔问题算法(c语言控制台动画演示版)
- 汉诺塔问题解决算法
- boj 1343汉诺塔 递归问题 多谢大牛的代码和讲解 我需要多联系类似题目
- 算法学习之最大子序列问题
- Java 线程同步问题 生产者-消费者 算法实现 -Java学习笔记(29)
- 【笔记】【算法学习】【动态规划】背包问题总结(1)
- 每日一算法,递推:汉诺塔问题
- 韩顺平_PHP程序员玩转算法公开课(第一季)01_算法重要性_五子棋算法_汉诺塔_回溯算法_学习笔记_源代码图解_PPT文档整理
- 算法导论学习笔记(十三):动态规划(三):01背包问题