辞职了,散分!并改进一下汉诺塔的算法。

kosora曹 2012-12-21 11:25:20
来公司一年多,经理就没接到什么像样的项目,大部分时间都是去支援别的项目组;我支援过测试、支援过对日外包、支援过CGI的项目、支援过.net......叫我干这些无聊的工作也就算了,经理还美其名曰“多学点东西啊,干软件的就要懂得东西多......”,我去,理由真好。唯一做过的一次JavaWeb项目,技术含量也不是很多,都是些数据库的CRUD和前台Dom的CRUD。
而且工资一直没涨,太憋屈,我就辞了。
吐槽结束。
为什么和大家一起来讨论汉诺塔呢?因为传说中,汉诺塔早在远古时期就和“世界末日”绑定在一起,今天就用这个帖子纪念我的第一次辞职和伟大的世界末日。
相信大家一定对汉诺塔有映像,我就不做过多的解释了。
以下是百度百科上的原文(http://baike.baidu.com/view/191666.htm):

一位法国数学家曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。
  不管这个传说的可信度有多大,如果考虑一下把64片金片,由一根针上移到另一根针上,并且始终保持上小下大的顺序。这需要多少次移动呢?这里需要递归的方法。假设有n片,移动次数是f(n).显然f(1)=1,f(2)=3,f(3)=7,且f(k+1)=2*f(k)+1。此后不难证明f(n)=2^n-1。n=64时,
  f(64)= 2^64-1=18446744073709551615
  假如每秒钟一次,共需多长时间呢?一个平年365天有 31536000 秒,闰年366天有31622400秒,平均每年31556952秒,计算一下,
  18446744073709551615/31556952=584554049253.855年
  这表明移完这些金片需要5845亿年以上,而地球存在至今不过45亿年,太阳系的预期寿命据说也就是数百亿年。真的过了5845亿年,不说太阳系和银河系,至少地球上的一切生命,连同梵塔、庙宇等,都早已经灰飞烟灭。


我想大家一定都能理解汉诺塔的递归算法,早在谭浩强(个人认为谭浩强和严蔚敏都是计算机教育界的顶尖大师)的书上,汉诺塔递归以及斐波那契的递归是作为范例讲的;以下是Java版汉诺塔的递归源码:

public void hannoi(int num,String from,String with,String to)
{
if(num==1){
//递归出口!
System.out.println("盘子:"+1+from+">>>>"+to);
}else{
hannoi(num-1,from,to,with);
System.out.println("盘子:"+num+from+ ">>>>" +to);
hannoi(num-1,with,from,to);
}
}
public static void main(String[] args) {
Hannoi h = new Hannoi();
h.hannoi(3, "A","B","C");
}


用文字描述“某一趟汉诺移动”,算法是这样的:
1、对于第num个盘子的“汉诺移动”,都需要传入三个参数 盘子号num、开始柱from、借助柱with、目标柱to;
2、先把本次的from柱作为num-1个盘子的from柱,本次to柱作为num-1个盘子的with柱,本次的with柱作为num-1个盘子的to柱,对num-1个盘子进行“汉诺移动”;
3、再把本次的盘子从from柱移动到to柱;
4、然后,由于本次的盘子已经在to柱,而num-1个盘子全在with柱,所以需要把num-1个盘子从with柱借助于from柱移动to柱,即:hannoi(num-1,with,from,to)
用递归描述起来似乎很简单,但对于某些逻辑思维不强的人来说,他们还是一知半解,主要会出现以下两个疑问:
1、虽然能用程序写出来,但程序的内部是如何进行构建和运行的呢?
2、如果手边只有一张纸、一支笔,没有VisualStadio、没有Eclipse、甚至没有NotePad,如何
用最短的时间把解决步骤写出来?
下面,听我来详细解释。
一、把算法和我们接触过的模型结合起来。
仔细观察一下汉诺塔的递归算法,就会发现,它和二叉树的中序遍历基本一致。
中序遍历的文字描述:
若二叉树为空则结束返回,
  否则:
  (1)中序遍历左子树。
  (2)访问根结点。
  (3)中序遍历右子树。
Java版算法:

class TreeNode{
  public int data;
  public TreeNode leftChild;
  public TreeNode rightChild;
  public static void inOrderTraversal(TreeNode node){
  if(node == null){
  return;
  }else{
  inOrderTraversal(node.leftChild);
  System.out.println(node.data);
  inOrderTRaversal(node.rightChild);
  }
  }
  }

我记得数据结构的“划重点课”上(前面的课我都没咋去),老师曾经说过这样一句话:
二叉树的先序遍历、中序遍历、后序遍历、层序遍历四种遍历是所有树和图、所有非线性算法的基础和原理,必须牢牢掌握这四种算法的递归(层序遍历一般不递归)以及非递归;掌握不了这四种算法,就无法掌握高级的排序和查找,也无法学好下学期的算法课,写的程序就永远是入门的、线性的、低效率的。
以前我没怎么好好学习数据结构,也不太理解他的这句话,导致我以后的算法课是抄别人的抄过的,毕设也是抄的,后来又被学校推荐到了一个不太好的公司,接着后面工作无聊,辞职......看来老师说的真对。
回到我们的汉诺塔话题,结合二叉树的中序遍历,我们很容易就画出3阶汉诺塔的空间递归树:

中序遍历这个二叉树,遍历某个节点时,输出from和to,with无须输出,就可以得到3阶汉诺塔的移动顺序:
盘子:1A>>>>C
盘子:2A>>>>B
盘子:1C>>>>B
盘子:3A>>>>C
盘子:1B>>>>A
盘子:2B>>>>C
盘子:1A>>>>C
可以看到,汉诺塔算法所生成的二叉树是一个相当完美的“完全二叉树”,所以它的总节点数是2^3-1=7个。
二、先简化一下算法;通过层序遍历,构建双链表;顺序遍历双链表,输出!
1、再仔细观察一下递归树,例如第一层的节点:A--null--C,“分裂成”了第二层的两个节点:A--C--B和B--A--C,而第二层的第一个节点A--C--B,又“分裂成”了:A--null--C和C--null--B。简化一下:如果不考虑with柱,只考虑from和to柱,AC可以分裂成AB和BC,AB可以分裂成AC和CB......相信你已经看出规律了,所以可以进一步简化为下面的递归树:



2、通过队列来构造双链表:
为了实现算法,你需要写这样一个小函数:
private static final String str="ABC";
public String getWith(String from,String to){
String with;
//找出ABC中的某个和from、to都不相等的字符串,赋值给with
return with;
}
你还需要有一个节点类:

/**
* 汉诺塔节点
*
*/
public class HanNode {

/**
* 盘子的编号
*/
private int num;

/**
* from柱
*/
private String from;

/**to柱
*
*/
private String to;

/**
* 双链表中的前驱结点
*/
private HanNode pre;

/**
* 双链表中的后继结点
*/
private HanNode next;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public HanNode getPre() {
return pre;
}
public void setPre(HanNode pre) {
this.pre = pre;
}
public HanNode getNext() {
return next;
}
public void setNext(HanNode next) {
this.next = next;
}
public HanNode(int num, String from, String to) {
super();
this.num = num;
this.from = from;
this.to = to;
}
public HanNode() {
super();
}
@Override
public String toString() {
return " [盘子:" + num + ", 从:" + from + "到:" + to + "]";
}

}

你还需要设置两个全局变量top和last作为双链表的头和尾,为了和其他的节点区分开来,设置它们的num为0;
下面的任务就是通过队列来构造双链表,对于某一趟运算:
①取出一个节点currentNode出队列,把这个currentNode的from和to传入getWith()函数,然后生成两个新的节点leftNode{num=currentNode.getNum()-1,from=from,to=with}和rightNode{num=currentNode.getNum()-1,from=with,to=to},压入队列。
②把生成的leftNode和rightNode插入到双链表中的currentNode的两端(需要断裂原来的链并生成新的链)
以第二层的第一个节点({n-1,A,B})为例,插入前是这个样子(为了清晰,没画头节点和尾节点):

插入后是这个样子:

如此不断的出队列、入队列、插链表.....直到队列的队头节点的num为1,循环终止。
3、遍历双链表,从top.getNext()开始,依次输出,如果遇到num为0的节点,表示已经到达尾部,此时循环终止。
PS:以上非递归算法,时间复杂度和递归算法一样;非递归算法生成了2^n-1个节点,而递归算法有2^n-1个的函数压入调用栈,所以非递归算法在空间复杂度上比递归算法小。

欢迎大家指正!
...全文
1476 50 打赏 收藏 转发到动态 举报
写回复
用AI写文章
50 条回复
切换为时间正序
请发表友善的回复…
发表回复
水木iiss 2013-10-29
  • 打赏
  • 举报
回复
很不错 接分
iskill2009 2013-01-08
  • 打赏
  • 举报
回复
ououpp,这种社会的败类,除了尽给中国人丢眼之外,根本不想中国社会有任何进步。 一遇到不同意见,就给别人戴上大顶帽子,接着再给自己上光环。 恶心!
ououpp 2013-01-06
  • 打赏
  • 举报
回复
引用 48 楼 iskill2009 的回复:
引用 46 楼 ououpp 的回复:干嘛说点什么就老拿人类地球宇宙毁灭作为最后结局那,真他妈庸俗无耻。 每个人都有选择属于自己生活的权力,只要不伤害到别人,只要不违法。别人移民外国或者到外国留学,关你鸟事!别人又不是用你的钱移民,又不是用你的钱送自己的儿女到外国留学,又不是跟你老婆私奔跑到外国去。你只不过是眼红别人有本事,能够四处走走,而你一辈子只能够呆在小山……
你要是个程序员才怪了,你干点什么不好,非要挣卖国这点小钱。
iskill2009 2013-01-03
  • 打赏
  • 举报
回复
引用 46 楼 ououpp 的回复:
干嘛说点什么就老拿人类地球宇宙毁灭作为最后结局那,真他妈庸俗无耻。
每个人都有选择属于自己生活的权力,只要不伤害到别人,只要不违法。别人移民外国或者到外国留学,关你鸟事!别人又不是用你的钱移民,又不是用你的钱送自己的儿女到外国留学,又不是跟你老婆私奔跑到外国去。你只不过是眼红别人有本事,能够四处走走,而你一辈子只能够呆在小山村里砍柴。“羡慕妒忌恨”是你这种人的本色!!!更何况这里是技术论坛,不是天牙之类的社会论坛,那里更适合你呆的地方。请你收起你的嘴,别在别人的技术贴上一味漫骂唠叨而且都是跟IT无关的东西,有病请吃药,药吃完了请去医院看医生。
paramorelove 2012-12-31
  • 打赏
  • 举报
回复
引用 43 楼 llllllll8ge 的回复:
楼主是男的 我试过。。
LZ 是男的 你试过 的意思是? 哦 好吧。。 我懂了~~~~
TrustTJM 2012-12-31
  • 打赏
  • 举报
回复
牛,收藏了!!!!
ououpp 2012-12-31
  • 打赏
  • 举报
回复
干嘛说点什么就老拿人类地球宇宙毁灭作为最后结局那,真他妈庸俗无耻。
8个l 2012-12-31
  • 打赏
  • 举报
回复
引用 44 楼 paramorelove 的回复:
引用 43 楼 llllllll8ge 的回复:楼主是男的 我试过。。 LZ 是男的 你试过 的意思是? 哦 好吧。。 我懂了~~~~
问过。。。
8个l 2012-12-31
  • 打赏
  • 举报
回复
楼主是男的 我试过。。
Storm-Shadow 2012-12-30
  • 打赏
  • 举报
回复
楼主还真是有心研究啊,我也得向你学习学习
YSC73211 2012-12-30
  • 打赏
  • 举报
回复
好难 ,以后懂了在看
不可笑的小丑 2012-12-29
  • 打赏
  • 举报
回复
高手,高手,膜拜,膜拜
gukuitian 2012-12-28
  • 打赏
  • 举报
回复
引用 35 楼 SmallYamateh 的回复:
引用 32 楼 gukuitian 的回复:引用 16 楼 SmallYamateh 的回复:引用 14 楼 textfire 的回复:如果我问一下,第23123步做的是什么动作?能够直接回答出来么? 至于“直接答出来”,我还得考虑考虑。 数组模拟二叉树。对应索引位 的确,有时候顺序表比链表更简单。
因为是定长的,没有空间浪费,也不用移动,还方便查找。
kosora曹 2012-12-27
  • 打赏
  • 举报
回复
引用 30 楼 dxqrr 的回复:
你的女神不错哦
是啊,而且是个认真的工科女哦!
dxqrr 2012-12-27
  • 打赏
  • 举报
回复
你的女神不错哦
leandzgc 2012-12-27
  • 打赏
  • 举报
回复
话说看不懂的说,标记一下,以后过来看。
y_keven 2012-12-27
  • 打赏
  • 举报
回复
好复杂
jackingod 2012-12-27
  • 打赏
  • 举报
回复
刚开始还以为是个女吊丝,唉,这年头程序猿伤不起呀。祝愿能找到好的工作。
笑莫问 2012-12-27
  • 打赏
  • 举报
回复
高手啊,看不懂了。。。
dreamhyz 2012-12-27
  • 打赏
  • 举报
回复
不错 ,顺便接分
加载更多回复(30)
16进制10进制.txt 32.txt asm.txt Crctable.txt C标志符命名源程序.txt erre.txt erre2.txt ff.txt for循环的.txt list.log N皇后问题回溯算法.txt ping.txt re.txt source.txt winsock2.txt ww.txt 万年历.txt 万年历的算法 .txt 乘方函数桃子猴.txt 乘法矩阵.txt 二分查找1.txt 二分查找2.txt 二叉排序树.txt 二叉树.txt 二叉树实例.txt 二进制数.txt 二进制数2.txt 余弦曲线.txt 余弦直线.txt 傻瓜递归.txt 冒泡排序.txt 冒泡法改进.txt 动态计算网络最长最短路线.txt 十五人排序.txt 单循环链表.txt 单词倒转.txt 单链表.txt 单链表1.txt 单链表2.txt 单链表倒序.txt 单链表的处理全集.txt 双链表正排序.txt 反出字符.txt 叠代整除.txt 各种排序法.txt 哈夫曼算法.txt 哈慢树.txt 四分砝码.txt 四塔1.txt 四塔2.txt 回文.txt 图.txt 圆周率.txt 多位阶乘.txt 多位阶乘2.txt 大加数.txt 大小倍约.txt 大整数.txt 字符串查找.txt 字符编辑.txt 字符编辑技术(插入和删除) .txt 完数.txt 定长串.txt 实例1.txt 实例2.txt 实例3.txt 小写数字转换成大写数字1.txt 小写数字转换成大写数字2.txt 小写数字转换成大写数字3.txt 小字库DIY-.txt 小字库DIY.txt 小孩分糖果.txt 小明买书.txt 小白鼠钻迷宫.txt 带头结点双链循环线性表.txt 平方根.txt 建树和遍历.txt 建立链表1.txt 扫描码.txt 挽救软盘.txt 换位递归.txt 排序法.txt 推箱子.txt 数字移动.txt 数据结构.txt 数据结构2.txt 数据结构3.txt 数组完全单元.txt 数组操作.txt 数组递归退出.txt 数组递归退出2.txt 文件加密.txt 文件复制.txt 文件连接.txt 无向图.txt 时间陷阱.txt 杨辉三角形.txt 栈单元加.txt 栈操作.txt 桃子猴.txt 桶排序.txt 检出错误.txt 检测鼠标.txt 汉字字模.txt 汉诺塔.txt 汉诺塔2.txt 灯塔问题.txt 猴子和桃.txt 百鸡百钱.txt 矩阵乘法动态规划.txt 矩阵转换.txt 硬币分法.txt 神经元模型.txt 穷举搜索法.txt 符号图形.txt 简单数据库.txt 简单计算器.txt 简单逆阵.txt 线性顺序存储结构.txt 线索化二叉树.txt 绘制圆.txt 编随机数.txt 网络最短路径Dijkstra算法.txt 自我复制.txt 节点.txt 苹果分法.txt 螺旋数组1.txt 螺旋数组2.txt 试题.txt 诺汉塔画图版.txt 读写文本文件.txt 货郎担分枝限界图形演示.txt 货郎担限界算法.txt 质因子.txt 输出自已.txt 迷宫.txt 迷宫问题.txt 逆波兰计算器.txt 逆矩阵.txt 逆阵.txt 递堆法.txt 递归桃猴.txt 递归车厢.txt 递推.txt 逻辑移动.txt 链串.txt 链栈.txt 链表十五人排序.txt 链表(递归).txt 链队列.txt 队列.txt 阶乘递归.txt 阿姆斯特朗数.txt 非递归.txt 顺序栈.txt 顺序表.txt 顺序队列.txt 骑士遍历1.txt 骑士遍历2.txt 骑士遍历回逆.txt 黑白.txt
1. C 语言中的指针和内存泄漏 5 2. C语言难点分析整理 10 3. C语言难点 18 4. C/C++实现冒泡排序算法 32 5. C++中指针和引用的区别 35 6. const char*, char const*, char*const的区别 36 7. C中可变参数函数实现 38 8. C程序内存中组成部分 41 9. C编程拾粹 42 10. C语言中实现数组的动态增长 44 11. C语言中的位运算 46 12. 浮点数的存储格式: 50 13. 位域 58 14. C语言函数二维数组传递方法 64 15. C语言复杂表达式的执行步骤 66 16. C语言字符串函数大全 68 17. C语言宏定义技巧 89 18. C语言实现动态数组 100 19. C语言笔试-运算符和表达式 104 20. C语言编程准则之稳定篇 107 21. C语言编程常见问题分析 108 22. C语言编程易犯毛病集合 112 23. C语言缺陷与陷阱(笔记) 119 24. C语言防止缓冲区溢出方法 126 25. C语言高效编程秘籍 128 26. C运算符优先级口诀 133 27. do/while(0)的妙用 134 28. exit()和return()的区别 140 29. exit子程序终止函数与return的差别 141 30. extern与static存储空间矛盾 145 31. PC-Lint与C\C++代码质量 147 32. spirntf函数使用大全 158 33. 二叉树的数据结构 167 34. 位运算应用口诀和实例 170 35. 内存对齐与ANSI C中struct内存布局 173 36. 冒泡和选择排序实现 180 37. 函数指针数组与返回数组指针的函数 186 38. 右左法则- 复杂指针解析 189 39. 回车和换行的区别 192 40. 堆和堆栈的区别 194 41. 堆和堆栈的区别 198 42. 如何写出专业的C头文件 202 43. 打造最快的Hash表 207 44. 指针与数组学习笔记 222 45. 数组不是指针 224 46. 标准C中字符串分割的方法 228 47. 汉诺塔源码 231 48. 洗牌算法 234 49. 深入理解C语言指针的奥秘 236 50. 游戏外挂的编写原理 254 51. 程序实例分析-为什么会陷入死循环 258 52. 空指针究竟指向了内存的哪个地方 260 53. 算术表达式的计算 265 54. 结构体对齐的具体含义 269 55. 连连看AI算法 274 56. 连连看寻路算法的思路 283 57. 重新认识:指向函数的指针 288 58. 链表的源码 291 59. 高质量的子程序 295 60. 高级C语言程序员测试必过的十六道最佳题目+答案详解 297 61. C语言常见错误 320 62. 超强的指针学习笔记 325 63. 程序员之路──关于代码风格 343 64. 指针、结构体、联合体的安全规范 346 65. C指针讲解 352 66. 关于指向指针的指针 368 67. C/C++ 误区一:void main() 373 68. C/C++ 误区二:fflush(stdin) 376 69. C/C++ 误区三:强制转换 malloc() 的返回值 380 70. C/C++ 误区四:char c = getchar(); 381 71. C/C++ 误区五:检查 new 的返回值 383 72. C 是 C++ 的子集吗? 384 73. C和C++的区别是什么? 387 74. 无条件循环 388 75. 产生随机数的方法 389 76. 顺序表及其操作 390 77. 单链表的实现及其操作 391 78. 双向链表 395 79. 程序员数据结构笔记 399 80. Hashtable和HashMap的区别 408 81. hash 表学习笔记 410 82. C程序设计常用算法源代码 412 83. C语言有头结点链表的经典实现 419 84. C语言惠通面试题 428 85. C语言常用宏定义 450

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧