栈+汉诺塔问题

栈的顺序存储结构

就是利用了数组和两个指针来实现的,base指针始终指向动态分配数组的第一个元素,而top指针则指向动态分配数组中最后一个元素的下一个空的位置,当我们Push元素的时候,元素就进入到了top所指的空间,top++这样top就又指向了空的空间,当然每次在push元素之前都要判断当前的空间是不是要满了,因为top最后所指的空间也会存东西的,此时的top就指向了数组外的空间了,此时top-base正好是stacksize,也就是说满了,在每次push之前都要判断top-base是不是等于stacksize来判断满没满,满了就再分配一个double satcksize的动态数组,把原来的东西全都复制过去,释放掉原来的动态数组,然后再往新的动态数组里面添加元素。

 出栈之前要判断栈里面是不是还有元素,如果top==base就证明栈空了,不空的话就是e=*(--top),这样就既取出了出栈元素又移动了top。

先进后出

------------------------------------------------------------------------------------

栈的链表实现

栈的链表实现就是#include"链表",然后在 栈.h的文件里面充分利用链表来在外部呈现栈的特性,链表要比栈灵活得多。

链表的头相当于栈顶,链表的尾部相当于栈底!【联想:把链表竖了起来,头节点那块在最上面】

 

栈的push操作就是插入元素,并且是插到第一个位置,内部直接insert(1,e)就可以了

栈的pop操作就是删除元素,直接删除掉第一个元素,内部直接Delete(1, e )就可以了。

栈的遍历就是链表的遍历,栈的遍历由top到base,正是因为这个原因,才把链表的头看成是top,尾部看成是base,因为这样在实现栈的遍历时我们就可以在其内部直接调用链表的遍历了。

------------------------------------------------------------------------------------------

栈的应用

栈的最大特点:先进后出!!!

①我们可以用其来实现数的进制转换:

  先得到个位, 入栈,再得到十位,入栈,低位先得到先进栈,高位后得到后进栈,然后在输出的时候,先得到高位,输出,再得到低位输出,这栈空了,也就输出完了。当然其中的进制转换也是需要考虑的一个很重要的环节(输出时用Switch语句来输出大于十的ABCDEF),书上的程序是十进制转换到2~16进制,其实可以实现2~16进制到2~16进制的转换,譬如一种思路是把要转换的数全都转换成十进制,然后再转换成2~16进制就可以了。

② 汉诺塔问题用栈来解决:

就是把待解决的问题不断地缩小规模,不断地入栈来实现的,

先说一下递归解法:


 有三个塔柱,分别为1,2,3  1上面有N个盘子

 

1.如果只有 1 个盘子,则不需要利用B塔,直接将盘子从A移动到C。
2.如果有 2 个盘子,可以先将盘子1上的盘子2移动到B;将盘子1移动到C;将盘子2移动到C。这说明了:可以借助B将2个盘子从A移动到C,当然,也可以借助C将2个盘子从A移动到B。
3.如果有3个盘子,那么根据2个盘子的结论,可以借助c将盘子1上的两个盘子从A移动到B;将盘子1从A移动到C,A变成空座;借助A座,将B上的两个盘子移动到C。
  以此类推,上述的思路可以一直扩展到 n 个盘子的情况,将将较小的 n-1个盘子看做一个整体,也就是我们要求的子问题,以借助B塔为例,可以借助空塔B将盘子A上面的 n-1 个盘子从A移动到B;将A最大的盘子移动到C,A变成空塔;借助空塔A,将B塔上的 n-2 个盘子移动到A,将C最大的盘子移动到C,B变成空塔…
 

 

①先把前1~N-1个盘子移到2号塔柱

②再将最后一个盘子移到3号塔柱

③最后将2号塔柱的N-1个盘子移到3号塔柱

 

然后递归问题还要安排出口,也就是当盘子数目是1的时候,这时应该直接将盘子移动到目标塔柱上面去:

 1 void hanoi(int n, int x, int y, int z)const
 2     {
 3         if(n==1)
 4             move(x, z, 1);
 5         else
 6         {
 7             hanoi(n-1, x, z, y);
 8             
 9             move(x, z, n);
10             hanoi(n-1, y, x, z);
11             
12         }
13     }

move(int x,int y,int n)表示将第n个盘子由塔座X移到塔座Y

hanoi(int n,int x,int y, int z)表示X上面有N个盘子,借助Y最终都移动到Z上面去

 再就是堆栈的解法,可以参考:

http://www.hawstein.com/posts/3.4.html

 递归解法的思路:

 我们自己完成压栈出栈操作,我们将打包的参数(代表着运行过程的某一个状态,也就是缩小版的要解决的问题)压入栈中,开始我们将原始问题压入栈中,接着我们就出栈操作,看看问题的规模有没有缩小到到最简单的出口情况,如果达到了,我们就执行出口操作。否则我们就将出栈的问题打碎成规模更小的问题,打包后执行一系列的入栈操作。这里入栈的顺序要与递归里面的顺序相反,因为只有入栈时顺序反了出栈时顺序才会正。然后我们再出栈,判断出栈的问题规模是不是达到了出口操作的条件,达到了就执行出口操作,否则就再把这个上一步打碎后的问题再进一步打碎,反向入栈,然后再出栈,如此往复。。。。

形象的理解可以这样来讲,原始问题可以看成是一大块石头,我们有一个类似于栈原理的容器譬如说是supstack(可以装石头,也就是任务容器),我们的目标是石头都打碎成直径小于X的颗粒才算是成功,。最开始的时候我们先将石头扔到superstack里面,然后从里面取出一个任务(也就是石头)我们发现这个石头太大不满足直径要求,我们就挥动斧头,将其劈成三大块扔进superstack里面,然后我们在从里面取出一块石头,判断其直径,还是不达到粒度要求,又拿出斧头,三斧下去变成了三块更小的石头,接着把他们扔进superstack里面,我们又从

superstack里面拿出了石头,又。。。。。直到这个过程的某一次我们取出的石头符合了直径要求,我们把它输出,譬如大喊“第一粒已生产成功”,接着我们再从superstack里面取出一块,再。。。。。。。。。。。

终有一天,superstack会变成空的,意味着我们的任务已经完成,可能石头已经变成了一袋子石粉。

当然,三板斧下去问题规模变小,我们将分解后的三块石头入栈是有次序问题的,现在拿汉诺塔问题来说如果入栈时不注重次序,最后肯定也会输出一系列的步骤,但这些步骤可能衔接是有问题的,但总的移动次数应该是没有问题的。

 

superstack如果是透明的,我们会看到从上往下,石头越来越大,问题规模越来越大,应该会呈现出与劈法有关系的一个整体趋势,如果一劈两半,那么就会呈现出石头甲下面的石头大小是他的两倍这种状况。我们的劈法精妙,问题解决就会快一些。
 

 

posted @ 2015-05-03 19:35  韩冰云  阅读(624)  评论(0编辑  收藏  举报