1、递归
1.1 树的高度
如果左子树和右子树的最大深度为 l 和 r,那么该二叉树的最大深度即为 max(l,r) + 1,左子树和右子树的最大深度又可以以同样的方式计算
# 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): if root == None: return 0 return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right))
时间复杂度:O(n),n 为二叉树节点的个数。每个节点被遍历一次
空间复杂度:O(height),height 表示二叉树的高度。递归函数需要栈空间,而栈空间取决于递归的深度。
1.2 平衡树
有了计算节点高度的函数,即可判断二叉树是否平衡。是平衡树的条件是当前左右子树的高度差小于等于 1,且左右子树分别是平衡树
# 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 isBalanced(self, root): def Depth(root): if root == None: return 0 return 1 + max(Depth(root.left), Depth(root.right)) if root == None: return True return abs(Depth(root.left) - Depth(root.right)) < 2 and self.isBalanced(root.left) and self.isBalanced(root.right)
1.3 归并两棵树
# 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 mergeTrees(self, t1, t2): if t1 == None: return t2 if t2 == None: return t1 root = TreeNode(t1.val + t2.val) root.left = self.mergeTrees(t1.left, t2.left) root.right = self.mergeTrees(t1.right, t2.right) return root
1.4 判断是否存在一条路径和等于一个数
# 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 hasPathSum(self, root, sum): if root == None: return False if root.left == None and root.right == None and root.val == sum: return True return self.hasPathSum(root.left, sum-root.val) or self.hasPathSum(root.right, sum-root.val)
1.5 统计路径和等于某个数的路径总数
# Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution(object): def pathSum(self, root, sum): def pathSum_from_root(root, sum): if root == None: return 0 res = 0 if root.val == sum: res += 1 res += pathSum_from_root(root.left, sum-root.val) + pathSum_from_root(root.right, sum-root.val) return res if root == None: return 0 return self.pathSum(root.left, sum) + self.pathSum(root.right, sum) + pathSum_from_root(root, sum)
1.6 子树
# Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution(object): def isSubtree(self, s, t): def issame(s, t): if s == None and t == None: return True if s == None or t == None: return False if s.val != t.val: return False return issame(s.left, t.left) and issame(s.right, t.right) if s == None: return False return issame(s, t) or self.isSubtree(s.left, t) or self.isSubtree(s.right, t)
1.7 判断树是否对称
# 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): def issame(s, t): if s == None and t == None: return True if s == None or t == None: return False if s.val != t.val: return False return issame(s.left, t.right) and issame(s.right, t.left) if root == None: return True return issame(root.left, root.right)
1.8 两节点间的最长路径
# 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 diameterOfBinaryTree(self, root): self.res = 0 def Depth(root): if root == None: return 0 L = Depth(root.left) R = Depth(root.right) self.res = max(self.res, L + R) return 1 + max(L, R) Depth(root) return self.res
1.9 翻转树
226. 翻转二叉树(Easy)
方法一:递归
# 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 invertTree(self, root): if root == None: return root node = root.left root.left = self.invertTree(root.right) root.right = self.invertTree(node) return root
方法二:迭代
中序遍历的思想,按照二叉树的层次,将每一行节点压入队列中,取出元素时交换左右子树
# 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 invertTree(self, root): if root == None: return root queue = [root] while queue: current = queue.pop(0) tmp_node = current.left current.left = current.right current.right = tmp_node if current.left: queue.append(current.left) if current.right: queue.append(current.right) return root
1.10 最小路径
方法一:深度优先搜索
求树的高度的变形,最小路径等于左子树的最小路径与右子树最小路径的较小值加1。注意:如果有一棵子树为空,只算另外一棵不为空的子树。
# 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 minDepth(self, root): if root == None: return 0 L = self.minDepth(root.left) R = self.minDepth(root.right) if L == 0 or R == 0: return 1 + L + R return min(L, R)+1
方法二:广度优先搜索
类似与层序遍历,当我们找到一个叶子节点时,直接返回这个叶子节点的深度。
# 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 minDepth(self, root): if root == None: return 0 queue = collections.deque([(root, 1)]) while queue: node, depth = queue.popleft() if node.left == None and node.right == None: return depth if node.left: queue.append([node.left, depth+1]) if node.right: queue.append([node.right, depth+1]) return 0
1.11 统计左叶子结点的和
方法一:递归
对于一个节点
- 如果它的左节点是叶子节点,累加,否则求左子树的左节点之和
- 求右子树的左节点之和
# 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 sumOfLeftLeaves(self, root): if root == None: return 0 left_leavers_sum = 0 if root.left: if root.left.left == None and root.left.right == None: left_leavers_sum += root.left.val else: left_leavers_sum += self.sumOfLeftLeaves(root.left) if root.right: left_leavers_sum += self.sumOfLeftLeaves(root.right) return left_leavers_sum
方法二:迭代
层序遍历,当节点为左节点且为叶子节点时,进行累加
将节点放入队列时,左节点标记为 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 sumOfLeftLeaves(self, root): left_leavers_sum = 0 if root == None or (root.left == None and root.right == None): return 0 queue = collections.deque([(root, 1)]) while queue: current, isleft = queue.popleft() if current.left == None and current.right == None and isleft: left_leavers_sum += current.val if current.left: queue.append([current.left, 1]) if current.right: queue.append([current.right, 0]) return left_leavers_sum
1.12 相同节点的最大路径长度
在递归函数中,首先对其左右子结点调用递归函数,得到其左右子树的最大相同值路径长度,接下来看当前结点和其左右子结点之间的关系了,如果其左子结点存在且和当前节点值相同,则左侧长度加 1,否则以当前节点为根节点的左侧相同节点的最大路径长度为 0;同理,如果其右子结点存在且和当前节点值相同,右侧长度加 1,否则为0。若该结点最长相同路径 left_arrow + right_arrow 大于 res,更新 res。
调用当前节点值的函数返回 left_arrow 和 right_arrow 中的较大值,原因如下:递归函数返回的是以该结点为终点的最长路径长度,这样回溯时,还可以继续连上其父结点。
# 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 longestUnivaluePath(self, root): self.res = 0 def arrow_len(root): if root == None: return 0 left_length = arrow_len(root.left) right_length = arrow_len(root.right) left_arrow, right_arrow = 0, 0 if root.left and root.left.val == root.val: left_arrow = left_length + 1 if root.right and root.right.val == root.val: right_arrow = right_length + 1 self.res = max(self.res, left_arrow + right_arrow) return max(left_arrow, right_arrow) arrow_len(root) return self.res
1.13 间隔层序遍历
将问题分解为偷根节点与不偷根节点,取二者最大值。偷根节点时,总钱数等于根节点钱数加上左右子树均不偷根节点钱数之和。
不偷根节点时,左右两颗子树可以偷根节点,也可以不偷,取两颗子树的最大值,加和。
left_rob 表示偷左子节点,left_no 表示不偷左子节点
right_rob 表示偷右子节点,right_no 表示不偷右子节点
# 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 rob(self, root): def _rob(root): if root == None: return 0, 0 left_rob, left_no = _rob(root.left) right_rob, right_no = _rob(root.right) return root.val + left_no + right_no, max(left_rob, left_no) + max(right_rob, right_no) return max(_rob(root))
1.14 二叉树中第二小的结点
如果根节点有子节点,则将左右结点的值保存,如果左节点值等于根节点的值,说明一定不是第二小的结点值,以左子节点为根节点寻找第二小的结点值(递归调用)。右侧也相同。在左右子树找到的第二小结点后进行比较,若都不为-1,返回较小的那个,若其中一个为-1,返回另外一个,否则不存在,返回-1。
# 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 findSecondMinimumValue(self, root): if root == None: return -1 if root.left == None and root.right == None: return -1 leftval = root.left.val rightval = root.right.val if leftval == root.val: leftval = self.findSecondMinimumValue(root.left) if rightval == root.val: rightval = self.findSecondMinimumValue(root.right) if leftval != -1 and rightval != -1: return min(leftval, rightval) if leftval != -1: return leftval return rightval
方法二:遍历,求非根节点值之外的最小值
也可以直接遍历,求非根节点值之外的最小值,这里我用了中序遍历,每遍历一层,将非根节点值保存,挑出最小值,如果遍历之后该值存在,则返回,不存在返回 -1
# 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 findSecondMinimumValue(self, root): queue = collections.deque() queue.append(root) res = float('inf') while queue: big = [] queue_len = len(queue) for _ in range(queue_len): current = queue.popleft() if current.val > root.val: big.append(current.val) if current.left: queue.append(current.left) queue.append(current.right) if big: big = sorted(big) res = min(res, big[0]) return -1 if res == float('inf') else res
也可以使用前序遍历,保存根节点的值为 small,第二小的数定义为 second,初始化为 -1,如果第一次遇见不等于根节点的数值,直接赋值给 second,不是第一次遇见不等于根节点的数值,如果比 second 小,更新 second
# 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 findSecondMinimumValue(self, root): self.second = -1 self.small = root.val def preview(root): if root == None: return if self.second == -1 and root.val != self.small: self.second = root.val if self.second != -1 and root.val != self.small: self.second = min(root.val, self.second) preview(root.left) preview(root.right) if root == None: return -1 preview(root) return self.second
1.15 二叉树中第二小的结点
令 f(N) 作为所有含 N 个结点的可能的满二叉树的列表。
每个满二叉树含有 3 个或更多结点,在其根结点处有 2 个子结点。这些子结点 left 和 right 本身就是满二叉树。因此,对于 N≥3,我们可以设定如下的递归策略:f(N) = [对于所有的 x,所有的树的左子结点来自 f(x) 而右子结点来自 f(N−1−x)]。
最后,我们应该缓存函数 f(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): memo = {0:[], 1:[TreeNode(0)]} def allPossibleFBT(self, N): if N in self.memo: return self.memo[N] res = [] for i in range(1, N, 2): for left in self.allPossibleFBT(i): for right in self.allPossibleFBT(N-i-1): root = TreeNode(0) root.left = left root.right = right res.append(root) return res """ :type N: int :rtype: List[TreeNode] """
2、层序遍历
2.1 二叉树的层序遍历
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def levelOrder(self, root: TreeNode) -> List[List[int]]: ans = [] if not root: return ans queue = [root] while queue: n = len(queue) level = [] for _ in range(n): child = queue.pop(0) level.append(child.val) if child.left: queue.append(child.left) if child.right: queue.append(child.right) ans.append(level) return ans
2.2 二叉树每层节点的平均值
# 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 averageOfLevels(self, root): res = list() queue = collections.deque() queue.append(root) while queue: len_queue = len(queue) sum = 0.0 for _ in range(len_queue): current = queue.popleft() sum += current.val if current.left: queue.append(current.left) if current.right: queue.append(current.right) res.append(sum/len_queue) return res
2.3 找树左下角的结点
从右到左的层序遍历,访问的最后一个元素就是左下角的节点
# 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 findBottomLeftValue(self, root): queue = collections.deque() queue.append(root) while queue: cur = queue.popleft() if cur.right: queue.append(cur.right) if cur.left: queue.append(cur.left) return cur.val
2.4 之字形打印二叉树
用两个栈分别保存从左向右和从右向左的元素。栈中每次保存二叉树一层的数据。
第 1,3,5 等奇数行的数据从左向右依次入栈,2,4,6 偶数行的数据从右向左依次入栈,后入栈的先出栈
# Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution(object): def preorderTraversal(self, root): res = list() stack = [] p = root while(p or stack): while(p): res.append(p.val) stack.append(p) p = p.left p = stack.pop() p = p.right return res
时间复杂度:O(n),其中 n 是二叉树的节点数,每一个节点恰好被遍历一次。
空间复杂度:O(n),为迭代过程中栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)
3、前中后序遍历
3.1 非递归实现二叉树前序遍历
144. 二叉树的前序遍历(Medium)
问题分析:
先序、中序和后序遍历过程:遍历过程中经过结点的路线一样,只是访问各结点的时机不同。
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def preorderTraversal(self, root: TreeNode) -> List[int]: res = [] stack = [] p = root while(p != None or len(stack) != 0): if p != None: res.append(p.val) stack.append(p) p = p.left else: top = stack.pop() p = top.right return res
3.2 非递归实现二叉树中序遍历
# Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution(object): def inorderTraversal(self, root): res = list() stack = [] p = root while(p or stack): while p: stack.append(p) p = p.left p = stack.pop() res.append(p.val) p = p.right return res """ :type root: TreeNode :rtype: List[int] """
3.3 非递归实现二叉树后序遍历
145. Binary Tree Postorder Traversal(Hard)
问题分析:
要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了 每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def postorderTraversal(self, root: TreeNode) -> List[int]: res = [] if root == None: return res stack = [] stack.append(root) cur = None pre = None while stack: cur = stack[-1] if (cur.left == None and cur.right == None) or ((cur.left == pre or cur.right == pre) and pre != None): res.append(cur.val) pre = cur stack.pop() else: if cur.right: stack.append(cur.right) if cur.left: stack.append(cur.left) return res