算法-树
一、二叉查找树-平衡树
1、前置知识:二叉树的深度
# 节点定义如下 # Definition for a binary tree node. class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None n3 = TreeNode(3) n9 = TreeNode(9) n20 = TreeNode(20) n15 = TreeNode(15) n7 = TreeNode(7) n3.left = n9 n3.right = n20 n20.left = n15 n20.right = n7 Solution().maxDepth(n3)
# 方法一:每到一个节点,深度就+1 class Solution: def maxDepth(self, root: TreeNode) -> int: return self.get_dep(root, 1) def get_dep(self, node, depth): if not node: return depth - 1 return max(self.get_dep(node.left,depth+1),self.get_dep(node.right,depth+1)) # 方法二: class Solution: def maxDepth(self, root: TreeNode) -> int: if root is None: return 0 else: left_depth = self.maxDepth(root.left) right_depth = self.maxDepth(root.right) return max(left_depth,right_depth)+1
算法解析:
终止条件: 当 root 为空,说明已越过叶节点,因此返回 深度 0 。
递推工作: 本质上是对树做后序遍历,从叶子节点一层层向上加
计算节点 root 的 左子树的深度 ,即调用 maxDepth(root.left);
计算节点 root 的 右子树的深度 ,即调用 maxDepth(root.right);
返回值: 返回 此树的深度 ,即 max(maxDepth(root.left), maxDepth(root.right)) + 1。
2、判断这个二叉树是否为平衡二叉树
# 方法一 class Solution: def isBalanced(self, root: TreeNode) -> bool: if not root: # 空树也是平衡树 return True left_height = self.get_dep(root.left, 1) right_height = self.get_dep(root.right, 1) if abs(left_height-right_height) > 1: return False else: return self.isBalanced(root.left) and self.isBalanced(root.right) def get_dep(self, node, depth): if not node: return depth - 1 return max(self.get_dep(node.left,depth+1),self.get_dep(node.right,depth+1)) # 方法二 class Solution: def isBalanced(self, root: TreeNode) -> bool: if not root: return True left_height = self.depth(root.left) right_height = self.depth(root.right) if abs(left_height - right_height) > 1: return False else: return self.isBalanced(root.left) and self.isBalanced(root.right) def depth(self, root): if not root: return 0 return max(self.depth(root.left), self.depth(root.right)) + 1
二、树的前中后序遍历
来源:力扣(LeetCode)
# 前序遍历 # 递归 # Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def preorderTraversal(self, root: TreeNode) -> List[int]: res = [] def tree(node): if not node: return res.append(node.val) tree(node.left) tree(node.right) tree(root) return res # 迭代 class Solution: """ 它先将根节点 cur 和所有的左孩子入栈并加入结果中,直至 cur 为空,用一个 while 循环实现: 然后,每弹出一个栈顶元素 tmp,就到达它的右孩子,再将这个节点当作 cur 重新按上面的步骤来一遍,直至栈为空。这里又需要一个 while 循环。 """ def preorderTraversal(self, root: TreeNode) -> List[int]: if not root: return [] cur, stack, res = root, [], [] while cur or stack: while cur: # 根节点和左孩子入栈 res.append(cur.val) stack.append(cur) cur = cur.left tmp = stack.pop() # 每弹出一个元素,就到达右孩子 cur = tmp.right return res # 中序遍历 # 递归 # 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]: res = [] def tree(node): if not node: return tree(node.left) res.append(node.val) tree(node.right) tree(root) return res # 迭代 class Solution: """ 和前序遍历的代码完全相同,只是在出栈的时候才将节点 tmp 的值加入到结果中。 """ def inorderTraversal(self, root: TreeNode) -> List[int]: if not root: return [] cur, stack, res = root, [], [] while cur or stack: while cur: # 根节点和左孩子入栈 stack.append(cur) cur = cur.left tmp = stack.pop() res.append(cur.val) # 出栈再加入结果 cur = tmp.right return res # 后续遍历 # 递归 # Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def postorderTraversal(self, root: TreeNode) -> List[int]: res = [] def tree(node): if not node: return tree(node.left) tree(node.right) res.append(node.val) tree(root) return res # 迭代 class Solution: """ 继续按照上面的思想,这次我们反着思考,节点 cur 先到达最右端的叶子节点并将路径上的节点入栈; 然后每次从栈中弹出一个元素后,cur 到达它的左孩子,并将左孩子看作 cur 继续执行上面的步骤。 最后将结果反向输出即可。参考代码如下: """ def postorderTraversal(self, root: TreeNode) -> List[int]: if not root: return [] cur, stack, res = root, [], [] while cur or stack: while cur: # 先达最右端 res.append(cur.val) stack.append(cur) cur = cur.right tmp = stack.pop() cur = tmp.left return res[::-1]
三、广度搜索算法
1、102. 二叉树的层序遍历
题目连接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
作者:JonnyHuang
链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/python3-er-cha-shu-ceng-xu-bian-li-by-jo-nlx3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
BFS(Breadth First Search)
JonnyHuang作者的思路
# 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 levelOrder(self, root: TreeNode) -> List[List[int]]: """ 步骤: 1.遍历当前层级,把当前层的所有节点的值存到结果列表中 2.再次遍历当前层级,把当前层的所有节点的下一层级(左右节点)添加到遍历列表中 3.重复1、2步 """ if not root: return [] queue = [root] # 当前层级的所有节点,默认从根节点开始 res = [] # 最终结果列表 while queue: res.append([cur_node.val for cur_node in queue]) # 步骤1 level = [] # 存储下一层级的所有节点 for node in queue: # 步骤2 if node.left: level.append(node.left) if node.right: level.append(node.right) queue = level return res
官方写法:
# 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]]: if not root: return [] res = [] stack = [root, ] # 进行遍历的节点顺序 while stack: level = [] # 每层的节点都存在一起 n = len(stack) # 每层需要处理的节点数 for _ in range(n): node = stack.pop(0) # 当前处理的节点 level.append(node.val) if node.left: stack.append(node.left) if node.right: stack.append(node.right) if level: res.append(level) return res
2、107. 二叉树的层序遍历 II
https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/
给定一个二叉树,返回其节点值自底向上的层序遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
# 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 levelOrderBottom(self, root: TreeNode) -> List[List[int]]: """ 步骤: 1.遍历当前层级,把当前层的所有节点的值存到结果列表中 2.再次遍历当前层级,把当前层的所有节点的下一层级(左右节点)添加到遍历列表中 3.重复1、2步 4.返回值可使用切片倒叙输出 """ if not root: return [] queue = [root] # 当前层级的所有节点,默认从根节点开始 res = [] # 最终结果列表 while queue: res.append([cur_node.val for cur_node in queue]) # 步骤1 level = [] # 存储下一层级的所有节点 for node in queue: # 步骤2 if node.left: level.append(node.left) if node.right: level.append(node.right) queue = level return res[::-1]
3、199. 二叉树的右视图
https://leetcode-cn.com/problems/binary-tree-right-side-view/
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
# 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 rightSideView(self, root: TreeNode) -> List[int]: """ 步骤: 1.遍历当前层级,右视图即每层最右边的值,即只需要对每一层的列表的最后一个元素入结果列表就可以了 2.再次遍历当前层级,把当前层的所有节点的下一层级(左右节点)添加到遍历列表中 3.重复1、2步 """ if not root: return [] queue = [root] # 当前层级的所有节点,默认从根节点开始 res = [] # 最终结果列表 while queue: res.append([cur_node.val for cur_node in queue][-1]) # 步骤1 level = [] # 存储下一层级的所有节点 for node in queue: # 步骤2 if node.left: level.append(node.left) if node.right: level.append(node.right) queue = level return res
四、深度搜索算法
1、102. 二叉树的层序遍历
https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
上面102题,我们也可以使用深度搜索算法
DFS(Depth First Search)不是按照层次遍历的。为了让递归的过程中同一层的节点放到同一个列表中,在递归时要记录每个节点的深度 level。递归到新节点要把该节点放入 level 对应列表的末尾。
作者:edelweisskoko
链接:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/102-er-cha-shu-de-ceng-xu-bian-li-die-da-bg9v/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
# Definition for a binary tree node. # class TreeNode(object): # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def levelOrder(self, root: TreeNode) -> List[List[int]]: """ 1.因为每层的节点值要分开记录,所以递归的参数除了节点node以外还需要当前层数level 2.如果节点已为空,return,结束当前递归分支即可 3.如果res的长度已经和当前层数level相等,说明res需要多加个位置了,因为level是res数组的索引,索引是一定比长度要小的,如果相等说明数组长度不够长了,得扩容 4.把当前节点加到对应层的数组中去res[level].append(node.val) 5.继续依次遍历左右字节点,层数level + 1 6.返回res """ res = [] def dfs(node, level): if not node: return if len(res) == level: res.append([]) res[level].append(node.val) dfs(node.left, level + 1) dfs(node.right, level + 1) dfs(root, 0) return res
2、207. 课程表
https://leetcode-cn.com/problems/course-schedule/
作者:jyd
链接:https://leetcode-cn.com/problems/course-schedule/solution/course-schedule-tuo-bu-pai-xu-bfsdfsliang-chong-fa/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution: def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: """ 三个标志位对应的状态 i == 0 : 干净的,未被 DFS 访问 i == -1:其他节点启动的 DFS 访问过了,路径没问题,不需要再访问了 i == 1 :本节点启动的 DFS 访问过了,一旦遇到了也说明有环了 """ def dfs(i, adjacency, flags): if flags[i] == -1: return True if flags[i] == 1: return False flags[i] = 1 for j in adjacency[i]: if not dfs(j, adjacency, flags): return False flags[i] = -1 return True # 初始化每个课程的依赖(列表) adjacency = [[] for _ in range(numCourses)] # 每个课程的标志位 flags = [0 for _ in range(numCourses)] # 填充完成每个课程的依赖 for cur, pre in prerequisites: adjacency[cur].append(pre) # 开始DFS去判断 for i in range(numCourses): if not dfs(i, adjacency, flags): return False return True