帮助我了解序遍历,而无需使用递归 [英] Help me understand Inorder Traversal without using recursion

查看:168
本文介绍了帮助我了解序遍历,而无需使用递归的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我能够理解preorder穿越不使用递归,但我有一个很难与序遍历。我只是似乎没有得到它,也许,因为我还没有明白递归的内部工作。

这是我到目前为止已经试过:

 高清traverseInorder(节点):
    后进先出法=礼佛()
    lifo.push(节点)
    而真正的:
        如果节点是没有:
            打破
        如果node.left不是无:
            lifo.push(node.left)
            节点= node.left
            继续
        preV =节点
        而真正的:
            如果节点是没有:
                打破
            打印node.value
            preV =节点
            节点= lifo.pop()
        节点= preV
        如果node.right不是无:
            lifo.push(node.right)
            节点= node.right
        其他:
            打破
 

内层while循环只是感觉不对。此外,一些元件的越来越打印两次;可能是我可以通过,如果该节点之前已经检查印刷解决这个问题,但是这需要另一个变量,这又感觉不对。我在哪里去了?

我没试过后序遍历,但是我想这是类似的,我也会面临同样的概念堵塞在那里。

感谢您的时间!

PS:定义的礼佛节点

 类节点:
    高清__init __(个体经营,价值,左=无吧=无):
        self.value =价值
        self.left =左
        self.right =右

类礼佛:
    高清__init __(个体经营):
        self.lifo =()
    高清推送(个体经营,数据):
        self.lifo =(数据,self.lifo)
    高清流行(个体经营):
        如果len(self.lifo)== 0:
            返回None
        RET,self.lifo = self.lifo
        返回RET
 

解决方案

开始用递归算法(伪code):

 移动(节点):
  !如果节点=无做:
    遍历(node.left)
    打印node.value
    遍历(node.right)
  ENDIF
 

这是尾递归的一个明确的情况下,这样你就可以轻松地把它变成一个while循环。

 移动(节点):
  !而节点=无做:
    遍历(node.left)
    打印node.value
    节点= node.right
  ENDWHILE
 

你留下了一个递归调用。什么是递归调用确实是推栈新的上下文中运行从一开始code,然后检索的背景和继续做它在做什么。因此,您可以创建一个栈存放和循环决定,在每次迭代中,无论我们是在一个第一次运行的局面(非空节点)或回归的局面(空节点,非空栈),并运行相应的code:

 移动(节点):
  堆栈= []
  而!空(栈)|| !节点=无做:
    如果节点=无做://这是一个正常的呼叫,递归
      推(栈,节点)
      节点= node.left
    否则//我们现在返回:弹出并打印当前节点
      节点=流行(栈)
      打印node.value
      节点= node.right
    ENDIF
  ENDWHILE
 

硬的东西要把握的是回归的一部分:你要确定,你的循环,你正在运行的code是否在输入功能的情况或从返回呼叫的情况,你将有一个的if / else 链尽可能多的情况下,你有非终端递归在code。

在此特定情况下,我们使用节点,以保持有关情况的信息。另一种方式是,以存储在堆栈本身(就像一个计算机做为递归)。有了该技术,在code是不太理想的,但更容易执行

 移动(节点):
  // 条目:
  如果节点== NULL做回报
  遍历(node.left)
  //后左跨越:
  打印node.value
  遍历(node.right)

遍历(结点):
   堆栈= [节点,'入门']
   !而空(栈)这样做:
     [节点,状态] = POP(栈)
     开关状态:
       案入门:
         如果节点==没有做的事:休息; // 返回
         推(堆栈,[节点,后 - 左 - 穿越'])//存储返回地址
         推(堆栈,[node.left,'入门'])//递归调用
         打破;
       案后,左穿越:
         打印node.value;
         //尾部调用:没有返回地址
         推(堆栈,[node.right,'入门'])//递归调用
      结束
    ENDWHILE
 

I am able to understand preorder traversal without using recursion, but I'm having a hard time with inorder traversal. I just don't seem to get it, perhaps, because I haven't understood the inner working of recursion.

This is what I've tried so far:

def traverseInorder(node):
    lifo = Lifo()
    lifo.push(node)
    while True:
        if node is None:
            break
        if node.left is not None:
            lifo.push(node.left)
            node = node.left
            continue
        prev = node
        while True:
            if node is None:
                break
            print node.value
            prev = node
            node = lifo.pop()
        node = prev
        if node.right is not None:
            lifo.push(node.right)
            node = node.right
        else:
            break

The inner while-loop just doesn't feel right. Also, some of the elements are getting printed twice; may be I can solve this by checking if that node has been printed before, but that requires another variable, which, again, doesn't feel right. Where am I going wrong?

I haven't tried postorder traversal, but I guess it's similar and I will face the same conceptual blockage there, too.

Thanks for your time!

P.S.: Definitions of Lifo and Node:

class Node:
    def __init__(self, value, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

class Lifo:
    def __init__(self):
        self.lifo = ()
    def push(self, data):
        self.lifo = (data, self.lifo)
    def pop(self):
        if len(self.lifo) == 0:
            return None
        ret, self.lifo = self.lifo
        return ret

解决方案

Start with the recursive algorithm (pseudocode) :

traverse(node):
  if node != None do:
    traverse(node.left)
    print node.value
    traverse(node.right)
  endif

This is a clear case of tail recursion, so you can easily turn it into a while-loop.

traverse(node):
  while node != None do:
    traverse(node.left)
    print node.value
    node = node.right
  endwhile

You're left with a recursive call. What the recursive call does is push a new context on the stack, run the code from the beginning, then retrieve the context and keep doing what it was doing. So, you create a stack for storage, and a loop that determines, on every iteration, whether we're in a "first run" situation (non-null node) or a "returning" situation (null node, non-empty stack) and runs the appropriate code:

traverse(node):
  stack = []
  while !empty(stack) || node != None do:
    if node != None do: // this is a normal call, recurse
      push(stack,node)
      node = node.left
    else // we are now returning: pop and print the current node
      node = pop(stack)
      print node.value
      node = node.right
    endif
  endwhile

The hard thing to grasp is the "return" part: you have to determine, in your loop, whether the code you're running is in the "entering the function" situation or in the "returning from a call" situation, and you will have an if/else chain with as many cases as you have non-terminal recursions in your code.

In this specific situation, we're using the node to keep information about the situation. Another way would be to store that in the stack itself (just like a computer does for recursion). With that technique, the code is less optimal, but easier to follow

traverse(node):
  // entry:
  if node == NULL do return
  traverse(node.left)
  // after-left-traversal:
  print node.value
  traverse(node.right)

traverse(node):
   stack = [node,'entry']
   while !empty(stack) do:
     [node,state] = pop(stack)
     switch state: 
       case 'entry': 
         if node == None do: break; // return
         push(stack,[node,'after-left-traversal']) // store return address
         push(stack,[node.left,'entry']) // recursive call
         break;
       case 'after-left-traversal': 
         print node.value;
         // tail call : no return address
         push(stack,[node.right,'entry']) // recursive call
      end
    endwhile 

这篇关于帮助我了解序遍历,而无需使用递归的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆