leetcode Tree 遍历基础整理 144& 94 & 145 &102

依次分别是二叉树的前序、中序、后序、层次遍历,除了层次遍历都有明确要求用循环而非递归做


144.前序遍历

直接给大家看discussion部分的优美代码吧,自己的就不拿出来丢人了…

class Solution(object):
    def preorderTraversal(self, root):
        ret = []
        stack = [root]
        while stack:
            node = stack.pop()
            if node:
                ret.append(node.val)
                stack.append(node.right)
                stack.append(node.left)
        return ret
很直观地(Empirically),我们使用栈

最后我们也会发现,由于栈具有后进先出的特点
在二叉树的循环遍历中,栈是实用度很高的

本题的注意细节就是:
  1. 节点从栈中弹出来后就立刻压入list,然后再压子节点到栈中

由于是先序遍历

  1. 存储栈的子节点的时候是先右再左

由于栈FILO


94.中序遍历

直接给大家看discussion部分的优美代码吧,自己的就不拿出来丢人了…

class Solution:
    def inorderTraversal(self, root):
        result, stack = [], [(root, False)]

        while stack:
            cur, visited = stack.pop()
            if cur:
                if visited:
                    result.append(cur.val)
                else:
                    stack.append((cur.right, False))
                    stack.append((cur, True))
                    stack.append((cur.left, False))

        return result

中序遍历跟前序比有点不一样的地方就是要考虑:
我们在将某个节点弹栈的时候,这个节点是否用完就可以丢弃(是否还需要再次入栈)
答案是显然的。虽然是中序遍历,我们在弹出该节点的时候可以将该节点的值压入返回的list中,但是我们还要将其存储在栈中用于保留其子节点返回的路径。
所以我们要进行标记以区分两次弹出(因为第二次弹出后该节点可被抛弃)

本题的注意细节就是:
  1. 弹栈的时候一定要先判断节点是否None(因为压栈的时候没有考虑)
  2. 存储栈的子节点的时候仍然是先右再左

由于栈FILO


145.后序遍历

后序遍历跟中序遍历的思想比较相似

class Solution:
    def inorderTraversal(self, root):
        result, stack = [], [(root, False)]

        while stack:
            cur, visited = stack.pop()
            if cur:
                if visited:
                    result.append(cur.val)
                else:
                    stack.append((cur.right, False))
                    stack.append((cur, True))
                    stack.append((cur.left, False))

        return result

大佬们还提出了另一种思路,就是在先序遍历的基础上进行修改:
(将先序的左右子节点压栈的顺序换下,然后最后reverse一下list就把先序变成后序了)

class Solution:
  # @param {TreeNode} root
  # @return {integer[]}
  def postorderTraversal(self, root):
      traversal, stack = [], [root]
      while stack:
          node = stack.pop()
          if node:
              # pre-order, right first
              traversal.append(node.val)
              stack.append(node.left)
              stack.append(node.right)

      # reverse result
      return traversal[::-1]
  • traversal[ : : -1]可以把返回列表反转后的copy (类似的,[::2]返回的就是list里面序号(从1开始计数)为偶数的元素

小总结:

这三个遍历都是以stack为主体,如果stack空了,那说明所有点遍历结束了,那么程序也可以结束了(所以外循环是while stack: )


102.层次遍历
def levelOrder(self, root):
    ans, level = [], [root]
    while root and level:
        ans.append([node.val for node in level])
        LRpair = [(node.left, node.right) for node in level]
        level = [leaf for LR in LRpair for leaf in LR if leaf]
    return ans
  • 亮点:
    1. 由于level需要存储的只是上一层level的东西,所以完全不需要层数这个参数,每次清空level即可
    2. 将我丑陋的if判断改成了双重循环
  • 列表生成式写嵌套循环:外层循环写在前面(这才符合”后面要用前面出现过的变量的“道理)
    • 但是列表生成器似乎不支持else语法
  • list.extend(list b) 相当于 list a + list b :在本列表内添加另一个列表的所有元素而非只添加一个元素(这个元素本身是个列表)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值