• 栈论 : 递归与栈式访问,如何用栈实现所有递归操作(幼儿园题目篇)


    上一篇 :

    栈论 : 递归与栈式访问,如何用栈实现所有递归操作(函数调用底层篇)

    2.用基础知识实现递归转栈式访问

    基于以上几点,我们怎么把所有的递归都用栈这个数据结构实现呢?

    想必聪明的你已经有想法了吧!

    例题 : (请先自己思考尝试一下怎么实现)

    先给出二叉树节点定义 :

    typedef struct BiTNode{
        char data;
        BiTNode* lchild,*rchild;
    } *BiTree;

    访问二叉树使用的函数 :

    void visit(BiTNode * node)

     

    如果觉得上述节点定义眼熟,那我们俩应该都是供液大学的。

    题1.难度等级: C

    使用栈实现二叉树的先序遍历

    函数头:

    void preOrderRead(BiTree tree);

     

    题2.难度等级: B

    使用栈实现二叉树的后续遍历

    void postOrderRead(BiTree tree);

     

    题3.难度等级: A

    二叉树的节点含有成员变量M

    找出二叉树中成员变量M为a 和 b 的节点的最小祖先节点(假设a b 只出现一次)

    BiTNode* findNearestAncestor(BiTree tree, char a, char b);

    题目1为例,思路开导与解析 :

    首先我们需要写出大概的使用递归实现功能的代码。

    题1:

    void preOrderRead(BiTree tree){
    
      if(tree == NULL){
    
        return;
    
      }
    
      visit(tree);//3
    
      preOrderRead(tree -> lchild); //1
    
      preOrderRead(tree -> rchild);//2
    
    }

    怎么转换成栈实现呢?

    从之前我们分析中知道,对函数的调用实际上是创建栈帧的过程,那么上图1,2处我们调用了两次函数,那么在这两处我们应该都要

    用创建栈帧来代替。

    问题是创建的栈帧里面应该包含什么内容呢?

    应该包含这个栈帧创建到销毁过程中所有需要的信息。而这里的信息可能不是直接获得的,例如可能我们的栈帧中包含了一个指向父栈帧的指针,那么我们就可以和父栈帧

    通信,而无需要把父栈帧中的某些变量之类的信息冗杂地包含到栈帧里来。

    严谨一点地说,栈帧应该包含本栈帧创建到销毁过程中需要的所有信息的 “来源或者信息本身”。

    还有更重要的一点,递归函数的方法体只有一个,也就是说,对说有的栈帧都要进行同一个操作,无论这个栈帧包含的信息有多么不一样!

    所以,方法中对栈帧的处理至关重要,他将作用于所有栈帧。

    要能够根据栈帧内包含的信息对信息不同的栈帧做出合理的操作。

    返回我们的题目1。除去1和2这两个创建栈帧的过程。如果把当前方法的调用想成一个栈帧,那么我们在栈帧里需要执行的操作只是判断本栈帧的节点是否为空,不空就读取,仅此而已。

    对应的,设计我们的函数实现.

    在这里,我们把栈的元素直接设计为节点,因为节点的信息已经够我们完成所有操作(只有visit操作而已);

    1.如果把栈帧的入栈想成函数调用,出栈想成函数返回,那么当栈为空的时候,函数调用就结束了。于是有了下面1处的判断栈是否是空的

    2.你可能会问:子函数都没调用完,2处怎么就把父函数的栈帧出栈了呢?因为如果我们在把子函数栈帧入栈(调用子函数)前将父函数的所有操作都做了,并且子函数的栈帧不需要和父函数栈帧通信的话,那么父函数的栈帧没有存在在栈中的意义了,因为该执行的都执行完了,子函数也不需要他,子函数在栈中的顺序也不会变,好一可怜的老父亲。

    在下面需要对栈帧做的所有操作只有visit,也就是访问他的节点,子函数栈帧入栈前(调用子函数)就可以把父函数的所有操作在3处完成了,没有其他操作要等待子函数栈帧出栈(返回)接着做,而且子函数的栈帧已经包含所有操作需要的信息了(BiTree),所以2处父栈帧直接出栈。

    4,5两处子函数栈帧入栈,表示父函数递归调用子函数。注意要放右孩子 rchild先,因为栈是先入后访问,而且左孩子总是先于右孩子访问

    void preOrderRead(BiTree tree){
    
       Stack stack;
    
       init(stack);
    
      stack.push(tree);
    
      while( ! empty(stack)){  // 1
    
        BiTNode* node = stack.pop();  // 2
    
              if(node == NULL){
    
          continue;
    
        }
    
         visit(node); // 3
    
           stack.push(tree -> rchild); // 4
    
        stack.push(tree -> lchild); // 5  
    
      }
    
    }

    下一篇 :

    栈论 : 递归与栈式访问,如何用栈实现所有递归操作(幼儿园题目篇,题目2)

     护眼绿:

    没人看的结语:

    首先很感谢你看到这里,辛苦了。

    文章中某些地方可能不正确或不准确,代码也可能不够高效可读,希望读者能够帮忙指正,共同学习进步。

  • 相关阅读:
    七种程序设计模式
    理清JavaScript正则表达式
    采用管道处理HTTP请求
    AngularJS之Service4
    Net Core-Razor
    中间件(Middleware)
    Redis集群明细文档(转)
    nginx 301跳转到带www域名方法rewrite(转)
    Redis启动多端口,运行多实例(转)
    web框架之Spring-MVC环境搭建(转)
  • 原文地址:https://www.cnblogs.com/lqlqlq/p/11790480.html
Copyright © 2020-2023  润新知