Tree Traversal
一般思路
- recursive
- 很简单,就不说了
- iterative, 用stack
- time
O(n)space是stack的高度O(logn)
- time
- Morris的方法!
- space是
O(1)的 - 这种做法主要是利用叶子节点中的有空指针指向中序遍历下的successor
- in-order和pre-order在方法上是一样的,只不过访问节点的时机不同
- space是
Morris的基本思想是
curr表示当前走到的node
- 如果curr没有left child, 将其输出,并且curr = curr.right
- curr有left child
- 找到其predecessor,在中序遍历就是左子树的rightmost child
- 将predecessor的right child设为curr,然后curr = curr.left
- 如果已经是curr了,说明左子树已经遍历完了
- 将right child重新设为None(恢复树结构)
- 输出curr
- curr = curr.right
时间复杂度
一开始以为是O(nlogn),因为找predecessor是logn。但是分析后我们可以发现,
每条边最多走两次:
- 定位到某个节点
- 寻找上面某个节点的predecessor
n个node的tree中有n-1条边,所以时间复杂度是O(n)
pre-order
iterative stack
def preOrder(root):
result = []
stack = []
curr = root
while curr or stack:
if curr:
stack.append(curr)
result.append(curr.val) # pre-order
curr = curr.left
else:
node = stack.pop()
curr = node.right
return result
Morris
def preOrder(root):
result = []
cur = root
pre = None
while cur:
if cur.left is None:
result.append(cur.val)
cur = cur.right
else:
pre = cur.left
while pre.right and pre.right != cur:
pre = pre.right
if pre.right is None:
result.append(cur.val)
pre.right = cur
cur = cur.left
else:
pre.right = None
cur = cur.right
return result
in-order
recursive
class Solution:
# @param A : root node of tree
# @return a list of integers
def inorderTraversal(self, A):
result = []
self.helper(A, result)
return result
def helper(self, A, result):
if not A:
return
self.helper(A.left, result)
result.append(A.val)
self.helper(A.right, result)
stack
依然是利用stack,不过这里node是在左子树所有node都输出之后才输出到result list上。我们要维护一个curr变量来记录走到的node(不是输出的node),输出永远 是当前node的左子树都放到stack上之后才从stack上pop输出
# Definition for a binary tree node
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# @param A : root node of tree
# @return a list of integers
def inorderTraversal(self, A):
result = []
stack = []
curr = A
while curr or stack: # cause curr can be None sometimes
if curr:
stack.append(curr)
curr = curr.left
else:
# left is done, can pop now
node = stack.pop()
result.append(node.val)
curr = node.right # 注意!
return result
Morris
class Solution:
# @param A : root node of tree
# @return a list of integers
def inorderTraversal(self, A):
result = []
curr = A
while curr:
if curr.left is None:
result.append(curr.val)
curr = curr.right
else:
# find predecessor in left subtree
prede = curr.left
while prede.right and prede.right != curr:
prede = prede.right
# use predecessor's right pointer
if prede.right != curr:
prede.right = curr
curr = curr.left
else:
# left subtree is done
prede.right = None
result.append(curr.val)
curr = curr.right
return result
post-order
stack
需要维护一个curr和pre来判断
class Solution:
# @param A : root node of tree
# @return a list of integers
def postorderTraversal(self, A):
result = []
stack = []
curr = A
pre = None
while curr or stack:
if curr:
stack.append(curr)
curr = curr.left
else:
peek = stack[-1]
if peek.right and peek.right != pre:
curr = peek.right # careful!
else:
stack.pop()
result.append(peek.val)
pre = peek
return result
Morris
也需要curr和pre,还要一个subrroutin将rightmost in lefttree到left child的path reverse 输出一下。
class Solution:
# @param A : root node of tree
# @return a list of integers
def postorderTraversal(self, A):
result = []
curr = A
dummy = TreeNode(0)
dummy.left = A
curr = dummy
while curr:
if curr.left is None:
curr = curr.right
else:
pre = curr.left
while pre.right and pre.right != curr:
pre = pre.right
if pre.right != curr:
pre.right = curr
curr = curr.left
else:
self.reverse(curr.left, pre)
tmp = pre
while tmp != curr.left:
result.append(tmp.val)
tmp = tmp.right
result.append(tmp.val)
self.reverse(pre, curr.left)
pre.right = None
curr = curr.right
return result
def reverse(self, start, end):
if start == end:
return
pre = start
curr = start.right
while pre != end:
n = curr.right
curr.right = pre
pre = curr
curr = n