二叉树系列1
- 二叉树理论基础
- 注意点小记
- 二叉树的种类
- 二叉树的存储方式
- 二叉树的遍历
- 要熟悉自己所用编程语言常用的数据容器的底层实现
- 一定要会自己实现所用数据结构的定义
- 二叉树的递归遍历
- 递归三部曲
- 前中后序递归遍历
- 前序遍历--我的代码
- 前序遍历--代码随想录的代码
- 中序遍历--我的代码
- 后序遍历--我的代码
- 二叉树的非递归遍历--迭代法
- 注意点记录
- 代码随想录强调
- 一刷逻辑还没有理清,对迭代法的思路没有掌握
- 代码随想录的代码
- 我的代码(当天晚上自己写)
- 二叉树的非递归遍历--迭代法统一编写方式
- 重点
- 代码随想录的代码
- 我的代码(当日晚上自己写)
- 二叉树的层序遍历
- 重点
- 代码随想录的代码
- 我的代码(当日晚上自己写)
- 用层序遍历打10个!
- 注:后面10题,均使用迭代法来进行层序遍历,递归版本的二刷再写。
- 102 就是层序遍历
- 107 二叉树的层序遍历 II
- 我的代码
- 199 二叉树的右视图
- 我的代码
- 637 二叉树的层平均值
- 429 N 叉树的层序遍历
- 我的代码
- 515 在每个树行中找最大值
- 我的代码
- 116 填充每个节点的下一个右侧节点指针
- 我的代码
- 代码随想录的代码
- 117 填充每个节点的下一个右侧节点指针 II
- 104 二叉树的最大深度
- 559 n叉树的最大深度
- 111 二叉树的最小深度
- 我的代码
- 226 翻转二叉树
- 注意
- 代码随想录的代码
- 我的代码(当日晚上自己写,不是独立完成,二刷要重视)
- 二叉树第一周小结
二叉树理论基础
先放上代码随想录的文章链接。
二叉树理论基础
注意点小记
二叉树的种类
1:完全二叉树底层不一定是满的,但是从左到右必须是连续的。
2:二叉搜索树:二叉搜索树的时间复杂度是O(logn),比如插入、查询。二叉搜索树对树的结构没有要求,对树的节点的顺序有一定要求。
3:满二叉树:满的
4、平衡二叉搜索树:左子树和右子树高度的绝对值的差不能大于1。
二叉树的存储方式
1:顺序存储,用一个数组去存储下标,如果父节点的数组下标是 i ,那么左孩子是 2i+1 , 右孩子是 2i+2 。在对二叉树进行顺序存储时,要先将二叉树扩充为完全二叉树,这样才满足左右孩子的计算方式。
2:顺序存储的方式很少用,一般均用链式存储,二叉树可以理解为就是一种链表,传入一个二叉树,就是传入一个链表。
二叉树的遍历
1:前序遍历、中序遍历、后序遍历:本质上均为深度优先搜索。先往深走,遇到叶子节点返回。DFS的实现大多用递归实现,而前中后序遍历,也均有两种实现方式(递归法、迭代法),用递归可以实现的,用栈也可以模拟递归的过程。
2:层序遍历:本质为广度优先搜索。实现上就是迭代法,用一个队列来实现,BFS的实现也是队列,先进先出的特性满足一层一层遍历的需求。
3:前中后序遍历,其实指的就是中间节点的遍历顺序。前–中左右,中:左中右,后:左右中。
要熟悉自己所用编程语言常用的数据容器的底层实现
下面是从网上找的一篇博客,讲的很详细。
使用Python实现常见的数据结构之原理讲解
一定要会自己实现所用数据结构的定义
这里是要会定义:二叉树的节点。(实现方式其实就是链表)
class TreeNode:
def __init__(self,val,left=None,right=None):
self.val = val
self.left = left
self.right = right
二叉树的递归遍历
递归遍历,学习如何写递归。递归三部曲将贯穿整个二叉树章节。
递归三部曲
1、确定递归函数的参数和返回值。(不必一次性就确定好,需要什么定义什么,二叉树章节一般为根节点+数组用于存储结果)
2、确定终止条件。
3、确定单层递归的逻辑。
前中后序递归遍历
放数组的操作即为:中
进左节点:左
进右节点:右
按照这个定义以及前中后序的逻辑,按顺序编写即可。
前序遍历–我的代码
对比之后还是觉得应该学习代码随想录的编写方式,不能因为函数中没有传入储存结果的变量,就去重新编写一个函数,还是对递归理解的不到位。
class TreeNode:
def __init__(self,val,left=None,right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
self.digui(root,res)
return res
def digui(self,root: Optional[TreeNode],res):
if root == None :
return
res.append(root.val)
self.digui(root.left,res)
self.digui(root.right,res)
前序遍历–代码随想录的代码
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
left = self.preorderTraversal(root.left)
right = self.preorderTraversal(root.right)
return [root.val] + left + right
中序遍历–我的代码
接下来的遍历,不再额外编写一个带存储结果变量的递归函数。
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None :
return []
left = self.inorderTraversal(root.left)
right = self.inorderTraversal(root.right)
return left + [root.val] + right
后序遍历–我的代码
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None :
return []
left = self.postorderTraversal(root.left)
right = self.postorderTraversal(root.right)
return left + right + [root.val]
二叉树的非递归遍历–迭代法
比较有难度!要好好理解。
代码随想录的讲解文章链接:
二叉树的迭代法遍历
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
注意点记录
前序–中左右:入栈顺序为–中右左,因为栈是先入后出。
中序遍历和前序遍历,是完全不一样的,不能用相同的逻辑!
中序遍历:用一个指针来遍历节点,用栈来记录遍历过的元素。
后序遍历:可以通过更改前序代码来实现。
代码随想录强调
在用迭代法实现遍历的过程中,要明确两个概念:遍历节点和处理节点,因为前序遍历时,二者一致,才可以较为简洁地写出代码。
仔细思考前序遍历的顺序–中左右,以及前序遍历的迭代法的迭代顺序(这里要好好思考一下,中间节点处理之后,push左右进去,但是下一层循环只处理一个,也就是说,在左右均可遍历的情况下,push两个pop一个),可以发现完全一致!
一刷逻辑还没有理清,对迭代法的思路没有掌握
一刷没有自己写的代码。
代码随想录的代码
前序遍历:
class TreeNode:
def __init__(self,val,left=None,right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
if root == None:
return []
stack = [root]
while stack != [] :
node = stack.pop()
res.append(node.val)
if node.right :
stack.append(node.right)
if node.left :
stack.append(node.left)
return res
后序遍历:
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None :
return []
res = []
stack = [root]
while stack != [] :
node = stack.pop()
res.append(node.val)
if node.left :
stack.append(node.left)
if node.right :
stack.append(node.right)
res.reverse()
return res
中序遍历:
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None:
return []
res = []
stack = []
cur = root
while cur or stack :
if cur :
stack.append(cur)
cur = cur.left
else :
cur = stack.pop()
res.append(cur.val)
cur = cur.right
return res
我的代码(当天晚上自己写)
前序遍历:
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None :
return []
stack = [root]
res = []
while stack :
node = stack.pop()
res.append(node.val)
# 这里注意,因为所用的stack为先进后出,所以先序遍历:中左右
# 是先加入右子树
if node.right :
stack.append(node.right)
if node.left :
stack.append(node.left)
return res
后序遍历:
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None :
return []
res = []
stack = [root]
while stack != [] :
node = stack.pop()
res.append(node.val)
if node.left :
stack.append(node.left)
if node.right :
stack.append(node.right)
res.reverse()
return res
中序遍历:(没写出来,错误代码)
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None :
return []
stack = []
res = []
cur = root
while cur or stack != [] :
if cur == None :
node = stack.pop()
res.append(node.val)
cur = node
else :
stack.append(cur.left)
cur = cur.left
二叉树的非递归遍历–迭代法统一编写方式
有困难,一刷不会。
重点
重点在于明确,只对中节点操作,即放入res中。
代码随想录的代码
前序遍历:
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st= []
if root:
st.append(root)
while st:
node = st.pop()
if node != None:
if node.right: #右
st.append(node.right)
if node.left: #左
st.append(node.left)
st.append(node) #中
st.append(None)
else:
node = st.pop()
result.append(node.val)
return result
中序遍历:
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st = []
if root:
st.append(root)
while st:
node = st.pop()
if node != None:
if node.right: #添加右节点(空节点不入栈)
st.append(node.right)
st.append(node) #添加中节点
st.append(None) #中节点访问过,但是还没有处理,加入空节点做为标记。
if node.left: #添加左节点(空节点不入栈)
st.append(node.left)
else: #只有遇到空节点的时候,才将下一个节点放进结果集
node = st.pop() #重新取出栈中元素
result.append(node.val) #加入到结果集
return result
后序遍历:
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
result = []
st = []
if root:
st.append(root)
while st:
node = st.pop()
if node != None:
st.append(node) #中
st.append(None)
if node.right: #右
st.append(node.right)
if node.left: #左
st.append(node.left)
else:
node = st.pop()
result.append(node.val)
return result
我的代码(当日晚上自己写)
前序遍历:注意处理逻辑,前序为中左右,换成栈的先入后出逻辑为,右左中。
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None :
return []
res = []
stack = [root]
while stack :
node = stack.pop()
if node == None:
temp = stack.pop()
res.append(temp.val)
else :
if node.right :
stack.append(node.right)
if node.left :
stack.append(node.left)
stack.append(node)
stack.append(None)
return res
中序遍历:
左中右,换算成栈为:右中左
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None :
return []
stack = [root]
res = []
while stack :
node = stack.pop()
if node == None :
res.append(stack.pop().val)
else:
if node.right :
stack.append(node.right)
stack.append(node)
stack.append(None)
if node.left :
stack.append(node.left)
return res
后序遍历:
左右中,换算成栈为:中右左
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if root == None :
return []
stack = [root]
res = []
while stack :
node = stack.pop()
if node == None :
res.append(stack.pop().val)
else :
stack.append(node)
stack.append(None)
if node.right :
stack.append(node.right)
if node.left :
stack.append(node.left)
return res
二叉树的层序遍历
没思路,直接对代码随想录的文章进行学习。需要明确:层序遍历用队列来实现。
重点
迭代法的理解较为直观。递归方法的理解:按照递归三部曲,确定处理逻辑即可。
代码随想录的代码
迭代法:
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
queue = collections.deque([root])
result = []
while queue:
level = []
for _ in range(len(queue)):
cur = queue.popleft()
level.append(cur.val)
if cur.left:
queue.append(cur.left)
if cur.right:
queue.append(cur.right)
result.append(level)
return result
递归法:
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
levels = []
self.helper(root, 0, levels)
return levels
def helper(self, node, level, levels):
if not node:
return
if len(levels) == level:
levels.append([])
levels[level].append(node.val)
self.helper(node.left, level + 1, levels)
self.helper(node.right, level + 1, levels)
我的代码(当日晚上自己写)
迭代法:
from collections import deque
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if root == None :
return []
result = []
dq = deque()
dq.append(root)
while dq :
size = len(dq)
res = []
for i in range(size):
node = dq.popleft()
res.append(node.val)
if node.left:
dq.append(node.left)
if node.right :
dq.append(node.right)
result.append(res)
return result
递归法:(第一遍错误代码)
如果加嵌套列表以及递归逻辑均错误。
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if root == None :
return []
res = []
self.digui(root,0,res)
return res
def digui(self,root,depth,res):
if root == None:
return
res[depth].append(root.val)
depth += 1
self.digui(root.left,depth,res)
depth -= 1
self.digui(root.right,depth,res)
depth -= 1
递归法:(改正后代码)
每次记得append空列表,depth不需要回退减一,因为depth是参数,回退之后自然就是正确的数值。
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if root == None :
return []
res = []
self.digui(root,0,res)
return res
def digui(self,root,depth,res):
if root == None:
return
if len(res)==depth:
res.append([])
res[depth].append(root.val)
depth += 1
self.digui(root.left,depth,res)
self.digui(root.right,depth,res)
用层序遍历打10个!
注:后面10题,均使用迭代法来进行层序遍历,递归版本的二刷再写。
102 就是层序遍历
107 二叉树的层序遍历 II
先获取层序遍历,后翻转。
我的代码
from collections import deque
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
if root == None :
return []
dq = deque()
dq.append(root)
res = []
while dq :
size = len(dq)
level = []
for i in range(size):
node = dq.popleft()
level.append(node.val)
if node.left :
dq.append(node.left)
if node.right:
dq.append(node.right)
res.append(level)
n = len(res)
left = 0
right = n-1
while left < right :
res[left],res[right] = res[right],res[left]
left += 1
right -= 1
return res
199 二叉树的右视图
难点在于,要想到用层序遍历,一直向right遍历是不行的,可能存在左子树比右子树深的情况。
我的代码
from collections import deque
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
if root == None :
return []
res = []
dq = deque()
dq.append(root)
while dq :
size = len(dq)
level = []
for i in range(size):
node = dq.popleft()
'''
这里不用每个都append进level里,甚至level都不需要声明
只需要 if i == size-1 , 直接append到res里就好了
'''
level.append(node.val)
if node.left :
dq.append(node.left)
if node.right:
dq.append(node.right)
res.append(level[-1])
return res
637 二叉树的层平均值
过。
429 N 叉树的层序遍历
我的代码
from collections import deque
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if root == None :
return []
result = []
dq = deque()
dq.append(root)
while dq :
size = len(dq)
res = []
for i in range(size):
node = dq.popleft()
res.append(node.val)
n = len(node.children)
for j in range(n):
dq.append(node.children[j])
result.append(res)
return result
515 在每个树行中找最大值
我的代码
class Solution:
def largestValues(self, root: Optional[TreeNode]) -> List[int]:
if root == None :
return []
result = []
dq = deque()
dq.append(root)
while dq :
size = len(dq)
maximum = -inf
for i in range(size):
node = dq.popleft()
if node.val > maximum :
maximum = node.val
if node.left:
dq.append(node.left)
if node.right :
dq.append(node.right)
result.append(maximum)
return result
116 填充每个节点的下一个右侧节点指针
我的代码
from collections import deque
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
if root == None :
return None
dq = deque()
dq.append(root)
while dq :
size = len(dq)
for i in range(size):
node = dq.popleft()
if i < size-1:
rnext = dq[0]
node.next = rnext
if node.left:
dq.append(node.left)
if node.right :
dq.append(node.right)
return root
代码随想录的代码
看上去更好一些,维护一个prev,不需要每次循环去调用 dq[0]
class Solution:
def connect(self, root: 'Node') -> 'Node':
if not root:
return root
queue = collections.deque([root])
while queue:
level_size = len(queue)
prev = None
for i in range(level_size):
node = queue.popleft()
if prev:
prev.next = node
prev = node
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return root
117 填充每个节点的下一个右侧节点指针 II
和116没有任何区别
104 二叉树的最大深度
层序遍历计数,过。
559 n叉树的最大深度
层序遍历计数,过。
111 二叉树的最小深度
需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点。
我的代码
from collections import deque
class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
if root == None :
return 0
dq = deque()
dq.append(root)
depth = 0
min_dep = inf
while dq :
size = len(dq)
depth += 1
for i in range(size):
node = dq.popleft()
if node.left == None and node.right == None :
if depth < min_dep :
min_dep = depth
else :
if node.left:
dq.append(node.left)
if node.right :
dq.append(node.right)
return min_dep
226 翻转二叉树
一刷没思路,直接学习了代码随想录的讲解。
注意
注意只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果
这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次!
这里自己画图试一试就可以理解,一刷通过画图已理解,如果硬要套中序遍历,是翻转两次 left 。
那么层序遍历可以不可以呢?依然可以的!只要把每一个节点的左右孩子翻转一下的遍历方式都是可以的!
代码随想录的代码
递归(前序遍历):
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
root.left, root.right = root.right, root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
递归(后序遍历):
略,二刷时编写。
递归法(中序遍历,两次 left):
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
self.invertTree(root.left)
root.left, root.right = root.right, root.left
self.invertTree(root.left)
return root
迭代法(DFS,前序遍历):
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
stack = [root]
while stack:
node = stack.pop()
node.left, node.right = node.right, node.left
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
return root
迭代法(DFS,后序遍历):
略,二刷时编写。
迭代法(DFS,中序遍历,两次 left):
略,二刷时编写。
迭代法(DFS,统一写法中序遍历,不需要两次 left):
无,注意交换处理逻辑的位置。
迭代法(BFS,层次遍历):
class Solution:
def invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
queue = collections.deque([root])
while queue:
for i in range(len(queue)):
node = queue.popleft()
node.left, node.right = node.right, node.left
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
return root
我的代码(当日晚上自己写,不是独立完成,二刷要重视)
递归(前序遍历):
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root == None:
return None
root.left , root.right = root.right , root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
递归法(中序遍历,两次 left):
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root == None:
return None
self.invertTree(root.left)
root.left, root.right = root.right, root.left
self.invertTree(root.left)
return root
迭代法(DFS,前序遍历):
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root == None :
return None
stack = [root]
while stack:
node = stack.pop()
node.left , node.right = node.right , node.left
if node.right :
stack.append(node.right)
if node.left:
stack.append(node.left)
return root
迭代法(DFS,后序遍历):
略,二刷时编写。
迭代法(DFS,中序遍历,两次 left):
略,二刷时编写。
迭代法(DFS,统一写法中序遍历,不需要两次 left):
注意交换处理逻辑的位置。
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root == None :
return None
stack = [root]
while stack :
node = stack.pop()
if node :
if node.right:
stack.append(node.right)
stack.append(node)
stack.append(None)
if node.left:
stack.append(node.left)
else :
node = stack.pop()
# 注意,交换处理逻辑的位置
node.left , node.right = node.right , node.left
return root
迭代法(BFS,层次遍历):
from collections import deque
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root == None :
return None
dq = deque()
dq.append(root)
while dq :
size = len(dq)
for i in range(size):
node = dq.popleft()
# 注意处理逻辑的位置
node.left, node.right = node.right, node.left
if node.left :
dq.append(node.left)
if node.right:
dq.append(node.right)
return root
二叉树第一周小结
代码随想录总结的很好很到位,直接上链接。
二叉树第一周小结