边工作边刷题:70天一遍leetcode: day 103
这两天开始重温leetcode经典题,发现再回过头来看很多问题都会有新的心得体会,leetcode的题真是要多过几遍才能融会贯通
Symmetric Tree Problem
这题是道easy题 (曾经linkedin电面时候秒过),但是如果没想清楚只背下题很快就会忘了。下面是题解的code (java)
- 基本的思路是递归配对。之所以可以用递归的方法是因为镜面对称的二叉树任意配对的node的left和right children是镜面对称的。这个条件可以作为invariant对整个树遍历。递归的基点是当前层两个已经配对的node,recurive function要以两个node的左右children作为参数
- 对于两个node的比较一共有4种情况,both not null, both null, only left null, only right null. 只有前两种case才有可能true (都不为null还要比较value)
- 递归里还是递归外?当前两个待比较node在递归里,只有两个node都不为null。才找对称点blindly进下一层
几乎完全一样code结构的还有Same Tree,不过因为两棵树,递归好想多了
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def isSymmetric(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
def symmetric(left, right):
if not left and not right:
return True
if not left or not right:
return False
if left.val!=right.val:
return False
return symmetric(left.left, right.right) and symmetric(left.right, right.left)
if not root: return True
return symmetric(root.left, root.right)
Binary Tree Level Order Traversal
基本题中的基本,这题的衍生题leetcode上就有一坨(比如臭名昭著的word laddar II),用bfs很直观,也可以用dfs。另外注意bfs有至少三种方式:2个queue,用一个queue+2个counter,一个queue+delimiter。曾经在亚麻的面试中被要求搞了三种。dfs看似效率不高,但是时间复杂度实际和bfs是一样的(O(n))。也要会,目测有衍生题会考这个点(隐约记得看过一个狗家的面经上有)
注意现在要求做到bug-free,有巨量的问题是有关循环的边界case是不是要额外处理。以2 counters的bfs为例,code里采用了前置循环,就是说起始值在循环外初始化(cur=1,root node入queue)。queue不为空,继续遍历当前层同时将下一层入queue。每一步出queue后检查是否当前层已经遍历结束来重置counter
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def levelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
res = []
if not root: return res
q = []
q.append(root)
curr = 1
while q:
next = 0
res.append([])
for i in range(curr):
cur = q.pop(0)
res[-1].append(cur.val)
if cur.left:
q.append(cur.left)
next+=1
if cur.right:
q.append(cur.right)
next+=1
curr = next
return res
Binary Tree Zigzag Level Order Traversal
算法结构类似level order traversal,根据题意下一层和当前层的遍历顺序是相反的,所以在展开阶段要顺序push下一层到stack里,这样下一层出栈的顺序就反过来了。另外,展开当前node是左到右还是右到左是每层交替的,所以要用一个变量来track。
错误点
- reverse是如何定的?因为reverse决定的是push的顺序。所以当前层如果是从左到右访问,那么下一层是从右向左(reverse),但是出stack的方向是反的,综上,reverse是False
- 这题因为是stack,所以不能用2层公用一个stack counting的方法做level traversal,必须用两个stack
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def zigzagLevelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
res = []
if not root: return res
reverse = False
stk = []
stk.append(root)
curr = 1
while stk:
next = []
res.append([])
while stk:
cur = stk.pop()
res[-1].append(cur.val)
if reverse:
if cur.right:
next.append(cur.right)
if cur.left:
next.append(cur.left)
else:
if cur.left:
next.append(cur.left)
if cur.right:
next.append(cur.right)
reverse = not reverse
stk = next
return res
Maximum Depth of Binary Tree
简单一题,但是递归结构可以用在很多Binary Tree的题目上(比如LCA):递归左右子树,然后比较左右返回结果取一返回
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def maxDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
def depth(root):
if not root: return 0
return 1+max(depth(root.left), depth(root.right))
return depth(root)
Construct Binary Tree from Preorder and Inorder Traversal
Construct Binary Tree from Inorder and Postorder Traversal
这两道题都是一个思路,比较tricky。以preorder/inorder为例,对于preorder遍历,一个子树对应一个连续的子数组,而首个元素就是root。问题就变成如何在子数组中进一步确定左右子树的边界,这样就可以进一步递归构建左右子树。inorder的序列可以用来找到边界:具体来说,因为无论是inorder还是preorder,子树都是数组中连续的元素,inorder的左右子树的分界点在root,这样如果能找到root的index,就能确定左子树的大小,进而确定在preorder序列里左子树的大小。这就是为什么我们要提前做invert index来存储value到index的map。而offset是用来跟踪当前在inorder序列里的左边界。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def buildTree(self, preorder, inorder):
"""
:type preorder: List[int]
:type inorder: List[int]
:rtype: TreeNode
"""
def build(inmap, preorder, low, high, offset):
if low>high: return None
rootVal = preorder[low]
m = inmap[rootVal]-offset
root = TreeNode(rootVal)
root.left = build(inmap, preorder, low+1, low+m, offset)
root.right = build(inmap, preorder, low+m+1, high, offset+m+1)
return root
inmap = {}
for i in xrange(len(inorder)):
inmap[inorder[i]]=i
return build(inmap, preorder, 0, len(preorder)-1, 0)```
Definition for a binary tree node.
class TreeNode(object):
def init(self, x):
self.val = x
self.left = None
self.right = None
class Solution(object):
def buildTree(self, inorder, postorder):
"""
:type inorder: List[int]
:type postorder: List[int]
:rtype: TreeNode
"""
def build(inmap, postorder, low, high, offset):
if low>high: return None
# if low==high: return TreeNode(postorder[high])
m = inmap[postorder[high]]-offset
root = TreeNode(postorder[high])
root.left = build(inmap, postorder, low, low+m-1, offset)
root.right = build(inmap, postorder, low+m, high-1, offset+m+1)
return root
inmap = {}
for i in range(len(inorder)):
inmap[inorder[i]]=i
return build(inmap, postorder, 0, len(postorder)-1, 0)