Leetcode刷题记录-图论BFS、DFS&二叉树-基础
一、二叉树节点定义代码实现:
class TreeNode:
def __init__(self, value, left = None, right = None):
self.value = value
self.left = left
self.right = right
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
二、种类:
满二叉树: 节点数 2^k - 1
完全二叉树:除了底层以外其他层都是满的,底层从左到右必须连续,共h层,则第h层包含1~2^(h - 1)个节点
二叉搜索树:有序树,左小右大,便于搜索,O(logn)
平衡二叉搜索树:空树或左右子树高度绝对值差不超过1,O(logn)
三、存储方式:
链式:链表
线式:数组,左右孩子:2i+1,2i+2
四、遍历及代码实现:
(同图论)
步骤:
① 确定递归函数的参数和返回值
② 确定终止条件
③ 确定单层递归的逻辑
⭐⭐二叉树代码完整实现:构建、传入
class TreeNode:
def __init__(self, val = 0, left = None, right = None):
self.val = val
self.left = left
self.right = right
# 根据数组构建二叉树
def construct_binary_tree(nums: []) -> TreeNode:
if not nums:
return None
# 用于存放构建好的节点
root = TreeNode(-1)
Tree = []
# 将数组元素全部转化为树节点
for i in range(len(nums)):
if nums[i]!= -1:
node = TreeNode(nums[i])
else:
node = None
Tree.append(node)
if i == 0:
root = node
# 直接判断2*i+2<len(Tree)会漏掉2*i+1=len(Tree)-1的情况
for i in range(len(Tree)):
if Tree[i] and 2 * i + 1 < len(Tree):
Tree[i].left = Tree[2 * i + 1]
if 2 * i + 2 < len(Tree):
Tree[i].right = Tree[2 * i + 2]
return root
# 算法:中序遍历二叉树
class Solution:
def __init__(self):
self.T = []
def inorder(self, root: TreeNode) -> []:
if not root:
return
self.inorder(root.left)
self.T.append(root.val)
self.inorder(root.right)
return self.T
# 验证创建二叉树的有效性,二叉排序树的中序遍历应为顺序排列
test_tree = [3, 1, 5, -1, 2, 4 ,6]
root = construct_binary_tree(test_tree)
A = Solution()
print(A.inorder(root))
⭐深度优先搜索DFS(BreadthFirstSearch):
前中后序遍历(栈:递归、迭代):
步骤:
① 确定递归函数的参数和返回值 :存放遍历过的节点
② 确定终止条件:为空就返回
③ 确定单层递归的逻辑:取节点的顺序,根据遍历顺序确定
1.前序遍历(144. 二叉树的前序遍历):
# 递归:
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
ans = []
def traversal(root):
if root == None: return
ans.append(root.val)
traversal(root.left)
traversal(root.right)
traversal(root)
return ans
# 迭代:进栈先放右孩子后放左孩子
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None: return []
stack = [root]
ans = []
while stack:
node = stack.pop()
ans.append(node.val)
if node.right: stack.append(node.right)
if node.left: stack.append(node.left)
return ans
2.中序遍历(94.二叉树的中序遍历):
# 递归:
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
ans = []
def traversal(root):
if root == None: return
traversal(root.left)
ans.append(root.val)
traversal(root.right)
traversal(root)
return ans
⭐访问顺序和处理顺序不一致,借助一个指针来访问节点,栈用于处理
# 迭代:
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None: return []
stack, ans = [], []
cur = root
while cur or stack:
# 先迭代访问最底层的左子树节点
if cur:
stack.append(cur)
cur = cur.left
# 到达最底左节点后,开始处理,入数组
else:
cur = stack.pop()
ans.append(cur.val)
cur = cur.right
return ans
3.后序遍历(145.二叉树的后序遍历):
# 递归:
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
ans = []
def traversal(root):
if root == None : return
traversal(root.left)
traversal(root.right)
ans.append(root.val)
traversal(root)
return ans
# 迭代:出栈顺序:中右左,然后将数组翻转,得到左右中
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None : return []
stack = [root]
ans = []
while stack:
node = stack.pop()
ans.append(node.val)
if node.left: stack.append(node.left)
if node.right: stack.append(node.right)
return ans[::-1]
4.前中后序统一迭代法:
入栈顺序完全按照遍历顺序的逆序
将需要处理的节点直接加入到栈中,中节点后面放入一个空节点, 只有空节点弹出的时候,才将下一个节点放进结果集
视频讲的很清楚:https://www.bilibili.com/video/BV1Zf4y1a77g/?spm_id_from=pageDriver&vd_source=0a8fa6e199761d9bcbc5d45a71832fed
中序遍历:
先访问所有节点并入栈,在中节点后添加一个None作为标记,只有遇到None才将节点放入结果
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
ans, stack = [], []
if root: stack.append(root)
while stack:
node = stack.pop()
# 先进右孩子后进左孩子
if node != None:
if node.right: # 空节点不入栈
stack.append(node.right)
stack.append(node)
stack.append(None) # 访问了中节点,存入栈后添加一个空为标记
if node.left: # 空节点不入栈
stack.append(node.left)
else:
node = stack.pop()
ans.append(node.val)
return ans
前序遍历:
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
ans, stack = [], []
if root: stack.append(root)
while stack:
node = stack.pop()
# 先进右孩子后进左孩子
if node:
if node.right: # 空节点不入栈
stack.append(node.right)
if node.left: # 空节点不入栈
stack.append(node.left)
stack.append(node)
stack.append(None) # 访问了中节点,存入栈后添加一个空为标记
else:
node = stack.pop()
ans.append(node.val)
return ans
后序遍历:
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
ans, stack = [], []
if root: stack.append(root)
while stack:
node = stack.pop()
if node:
stack.append(node)
stack.append(None)
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
else:
node = stack.pop()
ans.append(node.val)
return ans
⭐广度优先搜索BFS(BreadthFirstSearch):
队列,迭代,
层序遍历及变形x10道:
102.层序遍历Ⅰ
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
# deque来自collections模块,不在力扣平台时,需要手动写入
# deque相比list的好处是,list的pop(0)是O(n)复杂度,deque的popleft()是O(1)复杂度
from collections import deque
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
# 结果集
res = []
if not root:return res
que = deque([root])
while que:
# 每一层的遍历结果
ans = []
for i in range(len(que)):
cur = que.popleft()
ans.append(cur.val)
if cur.left: que.append(cur.left)
if cur.right: que.append(cur.right)
res.append(ans)
res.reverse()
return res
107.层序遍历Ⅱ
给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
相对于上一道,把结果集反转就可以了
from collections import deque
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
res = []
if not root:return res
que = deque([root])
while que:
ans = []
for i in range(len(que)):
cur = que.popleft()
ans.append(cur.val)
if cur.left: que.append(cur.left)
if cur.right: que.append(cur.right)
res.append(ans)
res.reverse()
return res
199. 二叉树的右视图
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
from collections import deque
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
if not root : return []
que = deque([root])
ans = []
while que:
# 每次只取最后一个
cur = que[-1]
ans.append(cur.val)
for i in range(len(que)):
cur = que.popleft()
if cur.left: que.append(cur.left)
if cur.right: que.append(cur.right)
return ans
637. 二叉树的层平均值
给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。
from collections import deque
class Solution:
def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
res = []
if not root:return root
que = deque([root])
while que:
size = len(que)
sum = 0
for i in range(size):
cur = que.popleft()
sum += cur.val
if cur.left:que.append(cur.left)
if cur.right:que.append(cur.right)
res.append(sum / size)
return res
429.N叉树的层序遍历
给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。
"""
# Definition for a Node.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
from collections import deque
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
if not root : return root
res = []
que = deque([root])
while que:
ans = []
for i in range(len(que)):
cur = que.popleft()
ans.append(cur.val)
if cur.children:
que.extend(cur.children)
res.append(ans)
return res
515. 在每个树行中找最大值
给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。
from collections import deque
class Solution:
def largestValues(self, root: Optional[TreeNode]) -> List[int]:
res = []
if not root:return res
que = deque([root])
while que:
ans = []
for i in range(len(que)):
cur = que.popleft()
ans.append(cur.val)
if cur.left:que.append(cur.left)
if cur.right:que.append(cur.right)
res.append(max(ans))
return res
116. 填充每个节点的下一个右侧节点指针
**给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。**二叉树定义如下:
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。
# 层序遍历
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
if not root: return None
que = [root]
while que:
n = len(que)
for i in range(n):
# 记录每层第一个节点
cur = que.pop(0)
if cur.left: que.append(cur.left)
if cur.right: que.append(cur.right)
if i == n - 1:
break
cur.next = que[0]
return root
# 链表
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
# 用first遍历每一层
first = root
while first:
cur = first
# cur 遍历每一层的节点
while cur:
if cur.left:cur.left.next = cur.right
if cur.right and cur.next :cur.right.next = cur.next.left
cur = cur.next
# 移动到下一层
first = first.left
return root
117. 填充每个节点的下一个右侧节点指针 II
给定一个二叉树,填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL 。初始状态下,所有 next 指针都被设置为 NULL 。
class Solution:
def connect(self, root: 'Node') -> 'Node':
if not root : return None
que = [root]
while que:
n = len(que)
tail = None
for i in range(n):
cur = que.pop(0)
# 让尾节点指向当前节点
if tail:tail.next = cur
# 让当前节点成为尾节点
tail = cur
if cur.left:que.append(cur.left)
if cur.right:que.append(cur.right)
return root
104. 二叉树的最大深度
给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。说明: 叶子节点是指没有子节点的节点。
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if not root:return 0
que = [root]
depth = 0
while que:
ans = []
for i in range(len(que)):
cur = que.pop(0)
ans.append(cur.val)
if cur.left:que.append(cur.left)
if cur.right:que.append(cur.right)
depth += 1
return depth
111. 二叉树的最小深度
给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。说明:叶子节点是指没有子节点的节点。
class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
if not root: return 0
que = [(root,1)]
while que:
cur,depth = que.pop(0)
# 左右节点都为空则返回答案
if cur.left == None and cur.right == None:return depth
if cur.left : que.append((cur.left,depth + 1))
if cur.right : que.append((cur.right,depth + 1))
return 0