94/ 144/145. Binary Tree Inorder/Preorder/Postorder Traversal (M/M/H)

Description (Inorder, 94):

Given a binary tree, return the inorder traversal of its nodes' values.

Example:

Input: [1,null,2,3]
1

2
/
3

Output: [1,3,2]

Follow up: Recursive solution is trivial, could you do it iteratively?


Solutions:

Trivial solution:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        if root == None:
            return []
        
        return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)

Runtime: 36 ms, faster than 67.60% of Python3 online submissions for Binary Tree Inorder Traversal.
Memory Usage: 13.1 MB, less than 70.72% of Python3 online submissions for Binary Tree Inorder Traversal.

None-trivial solution:

NOTE:
Maintain a stack, push root first, then every iteration, push left child first if existed.
If a left child is searched, result.append(current_parent_node's value), and left child = None, and search right child.
If a right child is searched, pop until a left child shows up.

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        if root == None:
            return []
        
        stack = [[root,True]]
        result = []
        while stack:
            # print('\n--------------------------------')
            # print(result)
            # for s in stack:
            #     print(s[0].val,s[1])
            cmp1 = stack[-1][0].left != None
            cmp2 = stack[-1][0].right != None
            if not cmp1 and not cmp2:
                result.append(stack[-1][0].val)
                left_or_right = stack[-1][1]
                stack.pop()
                while True:
                    if left_or_right == False:
                        stack[-1][0].left = None
                        break
                    if not stack:
                        break
                    else:
                        # stack[-1][0].right = None
                        left_or_right = stack[-1][1]
                        stack.pop()
            elif cmp1:
                stack.append([stack[-1][0].left,False]) 
                # stack 0th element is Node, 1st element is False if it's a left Node
            elif cmp2:
                result.append(stack[-1][0].val)
                stack.append([stack[-1][0].right,True])
                # 1st element is False if it's a right Node
        
        return result

Runtime: 32 ms, faster than 88.81% of Python3 online submissions for Binary Tree Inorder Traversal.
Memory Usage: 13.2 MB, less than 62.47% of Python3 online submissions for Binary Tree Inorder Traversal.

Better solution

inspired by https://zhuanlan.zhihu.com/p/65795759 and https://www.cnblogs.com/grandyang/p/4297300.html解法3

NOTE:

只要当前节点(stack[-1])不是None,就继续左转。如果是None,则先pop去掉最后一个None,然后保存最后一个元素(非None),继续pop这个元素,并把这个元素的right push进栈。

先左转保证“left”在顺序的最前面,“继续pop这个元素”是把parent放到顺序的第二位,再把right push进栈说明right child在顺序的最后。

链接里面的方法是建立一个curr一直等于当前搜索到的节点。如果是None就不push进栈,也就避免了pop两次

这种方法的精髓是,在遍历right child的时候,parent已经在stack中pop掉了,那如果right child遍历完成之后,关于right child下的节点都不会在stack里面,stack[-1]就是grandparent了。那么就不用要求node本身保存一个变量记住顺序,也不用单独在循环里保存一个True or False来记录之前左右转的历史情况。

# Code according to my understanding
class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        if root == None:
            return []
        
        stack = [root]
        result = []
        while stack:
            if stack[-1] != None:
                stack.append(stack[-1].left)
            else:
                stack.pop()
                if stack: # prevent the last step of traversal requiring to pop a empty stack
                    cache = stack.pop()
                    result.append(cache.val)
                    stack.append(cache.right)
        
        return result

Runtime: 28 ms, faster than 97.62% of Python3 online submissions for Binary Tree Inorder Traversal.
Memory Usage: 12.9 MB, less than 99.69% of Python3 online submissions for Binary Tree Inorder Traversal.

# rewrite the source code in Zhihu
class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        stack = []
        result = []
        curr = root
        while curr != None or stack:
            if curr != None:
                stack.append(curr)
                curr = curr.left
            else:
                curr = stack.pop()
                result.append(curr.val)
                curr = curr.right
        
        return result

Description (Preorder, 144):

Given a binary tree, return the preorder traversal of its nodes' values.

Example:

Input: [1,null,2,3]


Output: [1,2,3]
Follow up: Recursive solution is trivial, could you do it iteratively?


Solutions:

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        if root == None:
            return []
        return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)

Runtime: 32 ms, faster than 89.16% of Python3 online submissions for Binary Tree Preorder Traversal.
Memory Usage: 13.1 MB, less than 64.36% of Python3 online submissions for Binary Tree Preorder Traversal.

inspired by https://www.cnblogs.com/grandyang/p/4146981.html

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        ret = []
        stack = []
        curr = root
        while curr != None or stack:
            if curr != None:
                stack.append(curr)
                ret.append(curr.val) # moved from the "else" code block to the "if" code block
                curr = curr.left
            else:
                curr = stack.pop().right
                
        return ret

Runtime: 32 ms, faster than 89.16% of Python3 online submissions for Binary Tree Preorder Traversal.
Memory Usage: 13.1 MB, less than 83.48% of Python3 online submissions for Binary Tree Preorder Traversal.


Description (Postorder, 145):

Given a binary tree, return the postorder traversal of its nodes' values.

Example:

Input: [1,null,2,3]



Output: [3,2,1]

Follow up: Recursive solution is trivial, could you do it iteratively?


Solutions:

class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        if root == None:
            return []
        return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]

Runtime: 36 ms, faster than 66.25% of Python3 online submissions for Binary Tree Postorder Traversal.
Memory Usage: 13.2 MB, less than 43.46% of Python3 online submissions for Binary Tree Postorder Traversal.

花了4个小时搞出来的野路子Solution:

NOTE:
每次到一个新的节点了,stack push进右child和左child,但如果这俩都是None的话,就pop出stack元素,直到stack[-2]不是stack[-1]的parent,那么就继续pop一次,把当前这个子树都pop完,之后curr就成了下一个子树的父节点。

class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        if root == None:
            return []
        if root.left == None and root.right == None:
            return [root.val]
        ret = []
        stack = [root]
        curr = root
            
        while curr != None or stack:
            if curr != None:
                if curr.right != None:
                    stack.append(curr.right)
                if curr.left != None:
                    stack.append(curr.left)
                
            if curr != stack[-1]:
                curr = stack[-1]
            else:
                while len(stack) > 1 and (stack[-2].left == curr or stack[-2].right == curr):
                    ret.append(stack.pop().val)
                    curr = stack[-1]
                ret.append(stack.pop().val)
                if not stack:
                    break
                curr = stack[-1]

        return ret

Runtime: 36 ms, faster than 66.25% of Python3 online submissions for Binary Tree Postorder Traversal.
Memory Usage: 13.2 MB, less than 56.25% of Python3 online submissions for Binary Tree Postorder Traversal.

Solution: 重新写先序遍历的时候想到的,通过对stack这个数组进行操作的一种新方法

inspired by https://www.cnblogs.com/grandyang/p/4251757.html

由于后序遍历的顺序是左-右-根,而先序遍历的顺序是根-左-右,二者其实还是很相近的,我们可以先在先序遍历的方法上做些小改动,使其遍历顺序变为根-右-左,然后翻转一下,就是左-右-根啦

# 后序遍历
class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        ret = []
        stack = [root]
        curr = root
            
        while curr != None or stack:
            if curr != None:
                ret.append(curr.val)
                stack.pop()
                stack += [curr.left,curr.right]
                curr = curr.right
            else:
                stack.pop()
                if not stack:
                    break
                curr = stack[-1]
        return ret[::-1]
# 先序遍历
class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        ret = []
        stack = [root]
        curr = root
            
        while curr != None or stack:
            if curr != None:
                ret.append(curr.val)
                stack.pop()
                stack += [curr.right,curr.left] # left right 交换
                curr = curr.left # left right 交换
            else:
                stack.pop()
                if not stack:
                    break
                curr = stack[-1]
        return ret
# 中序遍历
class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        ret = []
        stack = [root]
        curr = root
            
        while curr != None or stack:
            if curr != None:
                stack.pop()
                stack += [curr.right,curr,curr.left]
                curr = curr.left
            else:
                if len(stack) == 1:
                    break
                ret.append(stack[-2].val)
                stack.pop()
                stack.pop()
                curr = stack[-1]
        return ret

Morris inorder traversal:

inspired by https://www.youtube.com/watch?v=wGXB9OWhPTg and
https://www.cnblogs.com/anniekim/archive/2013/06/15/morristraversal.html

The form of FindPredecessor is not shown here, so I implemented it by myself
class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        ret = []
        curr = root
        
        def FindPredecessor(node): # missing part of the picture
            cache = node.left
            while cache.right != None and cache.right != node: 
# searching predecessor for the second time will finally turn back to node itself => "and cache.right != node"
                cache = cache.right
            return cache
            
        while curr != None:
            if curr.left == None: 
# 左边是空,那么就pop,但是下一步可能往父节点的右边走,也可能父节点没有右边,则只需要回到父节点。
# 这里巧妙地把两种情况都先赋予了curr.right。
                ret.append(curr.val)
                curr = curr.right
            else:
                pred = FindPredecessor(curr)
                if pred.right == None: # 如果是第一次遍历到这个node
                    pred.right = curr
                    curr = curr.left
                # elif pred.right == curr: # 如果已经遍历过这个node
                else:
                    pred.right = None
                    ret.append(curr.val)
                    curr = curr.right
                    
        return ret

Runtime: 20 ms, faster than 99.99% of Python3 online submissions for Binary Tree Inorder Traversal.
Memory Usage: 13.2 MB, less than 44.95% of Python3 online submissions for Binary Tree Inorder Traversal.

Morris preorder traversal:

NOTE: 第一反应是把pred.right赋予curr.right,但是想了半天也没发现如何知道是否在移动到右侧的时候要把前置node.right归None。然后看了下https://www.geeksforgeeks.org/morris-traversal-for-preorder/
发现正确的搜索顺序和上面inorder一样,只需要把添加父节点的时间提前。

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        ret = []
        curr = root
        
        def FindPredecessor(node): # the same
            cache = node.left
            while cache.right != None and cache.right != node:
                cache = cache.right
            return cache
            
        
        while curr != None:
            if curr.left == None: # the same
                ret.append(curr.val)
                curr = curr.right
            else:
                pred = FindPredecessor(curr)
                if pred.right == None: # first time to meet a parent node
                    ret.append(curr.val) # moved from "else"
                    pred.right = curr
                    curr = curr.left
                else: # second time to meet, meaning that it's a searched node
                    curr = curr.right
                    pred.right = None
                
        return ret

Runtime: 36 ms, faster than 66.49% of Python3 online submissions for Binary Tree Preorder Traversal.
Memory Usage: 13.3 MB, less than 7.02% of Python3 online submissions for Binary Tree Preorder Traversal.

Morris postorder traversal:

class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        curr = root
        ret = []
        
        def FindPredecessor(node):
            cache = node.right
            while cache.left != None and cache.left != node:
                cache = cache.left
            return cache
        
        while curr != None:
            if curr.right == None:
                ret.append(curr.val)
                curr = curr.left
            else:
                pred = FindPredecessor(curr)
                if pred.left == None:
                    pred.left = curr
                    ret.append(curr.val)
                    curr = curr.right
                else:
                    curr = curr.left
                    pred.left = None

        return ret[::-1]
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335