报名了代码随想录往期的训练营(60天刷题计划),但是由于时间紧,所以联系报名已经开班20天的那期,准备每天刷两天的量,及时补上。还有很多其他事情,并且只能利用下班时间,昨晚12点睡,今早5点半就醒来,当然还是赖床了一会才起来工作,谢谢有想法就行动的自己。
因为day21是树相关的内容,我就从树开始的地方开始刷。边刷边做记录吧,多写一遍也不费很多时,毕竟以后忘了再想更费时。
1 基础知识点(day 14)
- 满二叉树、完全二叉树的概念
- 二叉搜索树、平衡二叉搜索树概念
- 二叉树的存储方式(顺序存储也是可以的)
- 遍历方式:深度优先(包括前中后序遍历)、广度优先(层次遍历)
- 二叉树初始化的代码表示
class TreeNode():
def __init__(self, value):
self.val = value
self.left = None
self.right = None
2 二叉树的递归遍历
写递归算法的步骤:
1. 确定递归函数的参数和返回值:确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型
2. 确定终止条件:运行的时经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
3. 确定单层递归的逻辑:把这个每次调用自身的函数写出来。
下面以先序遍历为例实际操作一下:
class Solution():
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
# 1. define the parameters of the traversal function and the return
ans = []
def Traversal(root):
# 2. specify the ending condition
if not root: return
# 3. write the workflow of the traversal
ans.append(root.val)
Traversal(root.left)
Traversal(root.right)
Traversal(root)
return ans
3 二叉树的非递归遍历
以先序遍历为例进行迭代遍历:
class solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root: 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
后序遍历的思路比较巧妙,先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序称为“中右左”,然后在反转result数组,输出的结果顺序就是“左右中”了。代码上就是最后三行上进行了改动,如下。
class solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root: 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 层序遍历(利用队列先进先出的规则)(day 15)
- 初始化:将根节点入队;
- 计算队列中元素个数即为本层结点数,从队列中弹出本层所有结点并将它的值记录在目标数组中,有左右孩子的将左右孩子入队;
- 重复步骤2直到队列为空。
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if not root: return []
from collections import deque
que = deque()
ans = []
que.append(root)
while que:
size = len(que)
level = []
for i in range(size):
a = que.popleft()
level.append(a.val)
if a.left: que.append(a.left)
if a.right: que.append(a.right)
ans.append(level)
return ans
5 LC226 翻转二叉树
用前/后序遍历最为自然。
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
# 1. 定义返回值,定义函数及其参数。发现和这个函数是一致的,就用自身就可以了。
# 2. 定义结束条件
if not root: return root
# 3. 描述递归的过程
root.left, root.right = root.right,root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
中序遍历有一个绕的地方:先左,然后在根节点交换左右孩子的时候,右孩子其实已经变成了左孩子,如果还写遍历右孩子的话实际上又去遍历左孩子了,那么右孩子一直没去处理,所以应该写成遍历左孩子(实际上是节点原来的右孩子)。但是还是用先/后序遍历比较自然。
class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
# 1. 定义返回值,定义函数及其参数。发现和这个函数是一致的,就用自身就可以了。
# 2. 定义结束条件
if not root: return root
# 3. 描述递归的过程
self.invertTree(root.left) # 左
root.left, root.right = root.right,root.left # 中
self.invertTree(root.left) #右
return root
层序遍历、非递归遍历(待更)
6 LC101 对称二叉树
判断左子树和右子树是否对称,即判断(1)左右子树的数值是否相等;(2)左孩子的左孩子==右孩子的右孩子;(3)左孩子的右孩子==右孩子的左孩子。
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
if not root: return False
def check(left,right):
if not left and not right: return True
if not left and right: return False
if left and not right: return False
return left.val == right.val and check(left.left, right.right) and check(left.right, right.left)
return check(root.left, root.right)
时间紧迫,因此只写了一种解法,后面如有时间二刷,可以把多种思路都尝试写一下。