1. 二叉树的递归写法和非递归写法
递归写法比较简单,非递归写法三种遍历不太一样比较难记,但是可以采用染色法来统一较好记。非递归前序和后序比较简单,考虑一下顺序即可,后序要逆转。中序先压栈左子树回到根部再右子树。
前序(leetcode 144)根左右
递归:
中序和后序类似写法,调整一下顺序即可。
# 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 preorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right) if root else []
非递归:
使用stack来实现。从top到bottom,从左到右。先将根节点压如栈内,弹出当前顶端的节点打印值,之后将当前节点的右孩子压入栈,左孩子压入栈,重复以上过程直到栈为空。
算法复杂度
时间复杂度:访问每个节点恰好一次,时间复杂度为 O(N),其中 N 是节点的个数,也就是树的大小。
空间复杂度:取决于树的结构,最坏情况存储整棵树,因此空间复杂度是 O(N)。
# 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 preorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
if not root: return []
ans = []
stack = []
stack.append(root)
while len(stack)!=0:
top = stack.pop()
if top.right:
stack.append(top.right)
if top.left:
stack.append(top.left)
ans.append(top.val)
return ans
染色法:
中序和后序类似,调整一下顺序即可。主要是分为已经访问的和还没有访问的节点。每个节点用一个元祖来记录,含其访问状态。先加入(根节点,未访问)到栈中。当栈不为空的时候,先弹出栈顶的节点,如果栈顶节点为空(叶子节点的左孩子或者右孩子),则继续循环。如果当前节点lu的访问状态为未访问,则按照要求的顺序倒序压入节点和访问状态;否则打印当前节点的值。
class Solution(object):
def preorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
if not root: return []
ans = []
stack = []
unvisited, visited = 0, 1
stack.append((root, unvisited))
while stack:
(top, flag) = stack.pop()
if not top: continue # 处理叶子节点的左孩子或者右孩子
if flag == unvisited:
stack.append((top.right, unvisited))
stack.append((top.left, unvisited))
stack.append((top, visited))
else:
ans.append(top.val)
return ans
中序 94
递归略,参照前序。
染色法略,参照前序。
非递归:
一直压入当前节点,找到树的最左边的孩子,直到当前节点为空。回退到上一层节点即弹出栈顶元素并且打印,并更新当前节点为其右孩子。符合中序根左右的顺序。
# 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 inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
if not root: return []
stack = []
ans = []
while stack or root:
while root:
stack.append(root)
root = root.left
root = stack.pop()
ans.append(root.val)
root = root.right
return ans
后序 145
非递归:
从根节点开始依次迭代,弹出栈顶元素输出到输出列表中,然后依次压入它的所有孩子节点,按照从上到下、从左至右的顺序依次压入栈中。
因为深度优先搜索后序遍历的顺序是从下到上、从左至右,所以需要将输出列表逆序输出。栈根左右,输出根右左,逆序左右根。
# 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 postorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
if not root: return []
ans = []
stack = []
stack.append(root)
while(stack):
top = stack.pop()
if top.left:
stack.append(top.left)
if top.right:
stack.append(top.right)
ans.append(top.val)
return ans[::-1]
层序遍历 102
采用队列的结构。
# 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]]
"""
if not root: return []
ans = []
q = [(root,0)]
while q:
curr, level = q[0]
if len(ans) == level:
ans.append([])
ans[level].append(curr.val)
q = q[1:] # dequeue
if curr.left:
q.append((curr.left, level+1))
if curr.right:
q.append((curr.right, level+1))
return ans
拓展:
104 二叉树的最大深度。
递归的思想,先考虑空的情况和根节点是叶子节点的情况。然后递归求左子树和右子树的最大深度,即可求得最大深度。
class Solution(object):
def maxDepth(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root: return 0
if not root.left and not root.right: return 1
return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
101 判断二叉树是不是对称的。
递归:思考对称的时候的条件!p, q两个节点是对称的,则其值相等,p左子树与q右子树是对称的。
class Solution(object):
def isSymmetric(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
return self.isMirror(root, root)
def isMirror(self, p, q):
if not p and not q:
return True
if not p or not q:
return False
return (p.val==q.val) and self.isMirror(p.left, q.right) and self.isMirror(p.right, q.left)
假设树上一共 n 个节点。
时间复杂度:这里遍历了这棵树,渐进时间复杂度为O(n)。
空间复杂度:这里的空间复杂度和递归使用的栈空间有关,这里递归层数不超过 n,故渐进空间复杂度为 O(n)
迭代:一般递归改迭代会用到栈或者队列。这里用队列。队列压入u,v两个需要比较的节点,每次弹出两个节点并进行比较,如果值不等则不对称,否则继续压入u的左子树和v的右子树,u的右子树和v的左子树。直至队列为空。
class Solution(object):
def isSymmetric(self, root):
"""
:type root: TreeNode
:rtype: bool
"""
if not root: return True
if not root.left and not root.right: return True
q = [root.left, root.right]
while q:
u = q[0]
v = q[1]
q = q[2:]
if not u and not v: continue
if not u or not v or u.val!=v.val: return False
q.append(u.left)
q.append(v.right)
q.append(u.right)
q.append(v.left)
return True
时间复杂度:O(n),同「方法一」。
空间复杂度:这里需要用一个队列来维护节点,每个节点最多进队一次,出队一次,队列中最多不会超过 n 个点,故渐进空间复杂度为 O(n)。
112 路径总和
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
递归:基本情况,如果叶子节点直接判断。否则递归判断左子树和右子树是否等于sum-root.val。
class Solution(object):
def hasPathSum(self, root, sum):
"""
:type root: TreeNode
:type sum: int
:rtype: bool
"""
# level traverse and record the sum!
if not root: return False
q = [(root, root.val)]
while q:
curr, curr_sum = q[0]
q = q[1:]
if not curr.left and not curr.right: # leaf node
if curr_sum == sum: return True
continue
if curr.left: # update left tree node
q.append((curr.left, curr_sum+curr.left.val))
if curr.right: # update right tree node
q.append((curr.right, curr_sum+curr.right.val))
return False
迭代:使用队列。层次遍历一遍树,并记录每个节点的路径和!
class Solution(object):
def hasPathSum(self, root, sum):
"""
:type root: TreeNode
:type sum: int
:rtype: bool
"""
# level traverse and record the sum!
if not root: return False
q = [(root, root.val)]
while q:
curr, curr_sum = q[0]
q = q[1:]
if not curr.left and not curr.right: # leaf node
if curr_sum == sum: return True
continue
if curr.left: # update left tree node
q.append((curr.left, curr_sum+curr.left.val))
if curr.right: # update right tree node
q.append((curr.right, curr_sum+curr.right.val))
return False
N叉树的前序、中序和后序遍历。
105 从二叉树的前序和中序构造树:和106类似。
106 从二叉树的后序和中序构造树:确定根,递归调用
class Solution(object):
def buildTree(self, inorder, postorder):
"""
:type inorder: List[int]
:type postorder: List[int]
:rtype: TreeNode
"""
if inorder == [] and postorder == []: return None
root_index = inorder.index(postorder[-1])
l_in = inorder[:root_index]
l_post = postorder[:root_index]
r_in = inorder[root_index+1:]
r_post = postorder[root_index:-1]
root = TreeNode(postorder[-1])
root.left = self.buildTree(l_in, l_post)
root.right = self.buildTree(r_in, r_post)
return root
思路一:层序遍历的另一种写法!
不采用(node,level)或者Null记录层次的方法,采用队列每层获取当成的size。时间和空间复杂度都是O(N)。
"""
# Definition for a Node.
class Node(object):
def __init__(self, val=0, left=None, right=None, next=None):
self.val = val
self.left = left
self.right = right
self.next = next
"""
class Solution(object):
def connect(self, root):
"""
:type root: Node
:rtype: Node
"""
if not root: return root
# level traverse
q = collections.deque()
q.append(root)
while q:
sz = len(q)
for i in range(sz):
curr = q.popleft()
if i < sz-1:
curr.next = q[0]
else:
curr.next = None
if curr.left:
q.append(curr.left)
if curr.right:
q.append(curr.right)
return root
思路二:分析指针之间的关系。分情况讨论:1.同一个子树的孩子。2.不同子树之间的孩子。只需要找到最左边的节点,知道父节点的next指针,可以以此更新其子节点的next指针。时间复杂度O(N),空间复杂度O(1)。
class Solution(object):
def connect(self, root):
"""
:type root: Node
:rtype: Node
"""
if not root: return root
leftmost = root
while leftmost.left:
head = leftmost # node at layer N
while head:
if head.left: # connection 1 at layer N+1
head.left.next = head.right
if head.next: # connection 2 at layer N+1,这里不是.right是因为最右边的节点和完美二叉树肯定会有右孩子。
head.right.next = head.next.left
head = head.next # move the node at layer N
leftmost = leftmost.left # move to layer N+1
return root
117 不是完全二叉树的情况。普通情况。
层序遍历一样的写法。
迭代要分情况考虑。我自己写的效率比较低。
class Solution(object):
def connect(self, root):
"""
:type root: Node
:rtype: Node
"""
if not root: return root
leftmost = root
while leftmost:
head = leftmost
while head:
# 4 conditions
if head.left and head.right:
head.left.next = head.right
head.right.next = self.getNextLeftChild(head)
head = head.next
continue
if head.left: # no right child
head.left.next = self.getNextLeftChild(head)
head = head.next
continue
if head.right: # no left child
head.right.next = self.getNextLeftChild(head)
head = head.next
continue
# no child
head = head.next
leftmost = self.getLeftMost(leftmost)
return root
def getLeftMost(self, curr):
if curr.left: return curr.left
if curr.right: return curr.right
leftmost = self.getNextLeftChild(curr) # 要return...
return leftmost
def getNextLeftChild(self, curr):
while curr.next:
if curr.next.left:
return curr.next.left
if curr.next.right:
return curr.next.right
curr = curr.next
return None
class Solution(object):
def connect(self, root):
"""
:type root: Node
:rtype: Node
"""
if not root: return root
leftmost = root
while leftmost:
dummy = Node(0) # 增加虚节点来记录下一层
prev = dummy # 下一层的指针
head = leftmost # 记录当前层的节点的指针
while head:
if head.left:
prev.next = head.left
prev = prev.next
if head.right:
prev.next = head.right
prev = prev.next
head = head.next
leftmost = dummy.next
# while dummy: # dummy就是下一层的整个链表
# print('ww')
# print(dummy.val)
# dummy = dummy.next
# while prev: # prev指针现在指向下一层最后一个节点
# print('yy')
# print(prev.val)
# prev = prev.next
return root
236 二叉树的最近公共祖先
题解2: lowestCommonAncestor这个函数不要理解为找公共祖先,而就理解为帮两个节点找祖先 传入的值是root, p, q,帮p和q找到一个祖先就行,找到两个就更好了,如果找不到就返回NULL 在root->left里面找一次,root->right里面再找一次,如果某一边返回值是NULL, 那么说明两个值都在另一边 由于找的时候,一定是找的最近的祖先返回,所以这里直接返回前面的返回值就行了,可以保证是最近的公共祖先 如果左右的返回值都不是NULL,那说明p和q分别在两边,则当前节点就是最近公共祖先 左右都找不到就直接返回NULL
递归法:分析基础状况,分析左子树和右子树。
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if not root: return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left and right: return root # 分割左右两个子树里面
if root == p or root == q: return root # 同在一侧的子树里面且其中一个结点就是root
if left: return left # 同在一侧的子树里面
if right: return right
return None
存储父结点(其实还是递归,但是换一种思路。)
先遍历获取父结点,从p开始记录哪些结点访问过,从q开始访问如果遇到访问过的即为共同祖先。
class Solution(object):
def __init__(self):
self.fa = dict()
self.visited = dict()
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
self.fa[root.val] = None
self.dfs(root) # traverse and save father nodes
while p:
self.visited[p.val] = True
p = self.fa[p.val] # move upward
while q:
if self.visited.has_key(q.val): return q
q = self.fa[q.val]
return None
def dfs(self, root):
if root.left:
self.fa[root.left.val] = root
self.dfs(root.left)
if root.right:
self.fa[root.right.val] = root
self.dfs(root.right)
tbc.