写代码的第十一天,进入二叉树了
二叉树的定义
class TreeNode:
def __init__(self, val, left = None, right = None):
self.val = val
self.left = left
self.right = right
递归遍历
思路
1、确定递归函数的参数和返回值;
2、确定终止条件
3、确定单层递归的逻辑
解决问题1:参数和返回值?我们将root根结点作为参数,进行遍历,设置一个空列表,用来存储最后的输出结果返回值。
解决问题2:终止条件是什么?从跟结点开始遍历,那么当遍历到空的时候停止遍历。
解决问题3:单层递归逻辑?前序遍历:中左右;中序遍历:左中右;后续遍历:左右中。
前序遍历:
错误第一版:res没跟新,每次遍历完没有加入到res中。
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
if root == None:
return res
res.append(root.val)
self.preorderTraversal(root.left)
self.preorderTraversal(root.right)
return res
正确版:
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
if root == None:
return res
res.append(root.val)
res += self.preorderTraversal(root.left)
res += self.preorderTraversal(root.right)
return res
后序遍历:
正确代码:
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
if root == None:
return res
res += self.postorderTraversal(root.left)
res += self.postorderTraversal(root.right)
res.append(root.val)
return res
中序遍历:
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
if root == None:
return res
res += self.inorderTraversal(root.left)
res.append(root.val)
res += self.inorderTraversal(root.right)
return res
迭代遍历(非递归)
一般来说都可以用栈实现递归的效果。
前序遍历(栈)
思路
首先要有一个栈,用栈来存储树中的结点,还需要一个数组作为最后的返回值输出。那么栈的作用其实就是一个存储中间转换值的作用,对于前序遍历来说是中左右的顺序,那么先入栈的是中,按理来说接下来入栈的应该是中的左孩子,然后才是有孩子,但是需要考虑一个问题栈的特性是后进先出,所以为了让最后的输出先是左孩子,再是右孩子,那么就需要在栈中先入栈右孩子,再入栈左孩子,这样从栈中输出的就先是左孩子,然后是右孩子。
错误第一版:错误原因,没有判断如果结点为空该怎么办。同理,下面的左右孩子结点也要判断是否为空。
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
stack.append(root.val)
num = stack.pop()
res.append(num)
stack.append(root.right.val)
stack.append(root.left.val)
numleft = stack.pop()
res.append(numleft)
numright = stack.pop()
res.append(numright)
return res
错误第二版:错误原因是当前的代码判断的都是root根结点的,如果根结点只有左孩子呢,或者只有右孩子呢,那么这个时候pop他无结点的那一部分就会报错,所以要用循环,要循环的是全部的结点,而不是root结点自己!!!!
但是循环的终止条件是什么?也就是所有结点全部使用完毕,可能第一想法是node.left或node.right为空那么停止循环,但是这个问题和前面一样,谁也不能保证这个结点有没有左右孩子结点,如果用这个作为循环条件会导致最后丢结点的。所以现在就只剩一个条件可以用了,也就是栈是否为空,如果栈已经为空了,那一定是所有元素都pop出来了。
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
if root == None:
return res
stack.append(root.val)
num = stack.pop()
res.append(num)
if root.right != None:
stack.append(root.right.val)
if root.left != None:
stack.append(root.left.val)
numleft = stack.pop()
res.append(numleft)
numright = stack.pop()
res.append(numright)
return res
错误第三版:这个测试用例无法通过,可以发现结点的数字不进循环呀,代码中虽然加入了循环,但是还是用的root结点这不对,我们使用的是当栈不为空的时候进入循环,在这段代码中我们将root加入栈但是随后我们就pop出来了,这时候栈一定是空的了!!!!!那么是根本不进入while循环的!!!!!!!所以要进入循环之后再存储和pop。而且还有一个问题,我们一直append进入栈的都是val,那么就无法用栈中的结点进行左右孩子的判断,所以append进入栈的应该是完整的结点,而不是单独的val。
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
if root == None:
return res
stack.append(root.val)
num = stack.pop()
res.append(num)
while len(stack) != 0:
if root.right != None:
stack.append(root.right.val)
if root.left != None:
stack.append(root.left.val)
numleft = stack.pop()
res.append(numleft)
numright = stack.pop()
res.append(numright)
return res
错误第四版:while循环中的if判断可以判断是否num的左右孩子为空在进行入栈操作,但是在这个if之后我们要进行结点的pop以及val值的append,那么也是需要判断pop时候stack是否为空,但是我们会发现,下面的四行pop结点append进res和上面while循环开始时要做的操作是一致的,我们不需要重新对左右孩子结点进行操作,所有的操作都是对结点的饿操作,只需要判断左右孩子结点是否存在即可,若存在按顺序输出。
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
if root == None:
return res
stack.append(root)
while len(stack) != 0:
num = stack.pop()
res.append(num.val)
if num.right != None:
stack.append(num.right)
if num.left != None:
stack.append(num.left)
numleft = stack.pop()
res.append(numleft)
numright = stack.pop()
res.append(numright)
return res
正确版本:
class Solution:
def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
if root == None:
return res
stack.append(root)
while len(stack) != 0:
num = stack.pop()
res.append(num.val)
if num.right != None:
stack.append(num.right)
if num.left != None:
stack.append(num.left)
return res
后序遍历(栈)
思路
在前序遍历的思路上,在栈这种数据结构中,我们的输入顺序是中,弹出中,然后输入右左,弹出左右,得到的顺序是中左右。对于后序遍历来说是左右中,那是不是将前序遍历反转一下就可以呢?我们将前序遍历简单修改一下,首先在栈中输入中,弹出中,然后输入左右,这样弹出的是右左,最后的结果就是中右左,然后反转一下也就变成了左右中,正好是后续遍历。
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
if root == None:
return res
stack.append(root)
while len(stack) != 0:
node = stack.pop()
res.append(node.val)
if node.left != None:
stack.append(node.left)
if node.right != None:
stack.append(node.right)
return res[::-1]
中序遍历(栈+指针)
前序和后序遍历都是首先访问的是哪个结点,那么首先处理的就是哪个结点,但是中序遍历不是,对于二叉树遍历来说,首先遍历的肯定是二叉树的根结点,但是在中序遍历中最先处理的却不是跟结点,也就是遍历结点和处理结点不一致,所以和上面两个遍历操作是不一样的。
思路
还是用栈实现,这里的栈是存储遍历过但还没用上的结点。首先设置一个指针,这个指针的作用是一直向下遍历,因为中序遍历就是左中右,所以要先找到最下面的左孩子,所以这个指针的停止条件就是,当他为空的时候就证明已经到了最下面的左孩子了,这个时候栈里面的元素就是指针遍历经过的所有元素了,所以此时左孩子为空的结点已经找到了,那么要遍历中结点了,那中结点其实就是这个指针的上一位结点,也就是在栈中存储的栈顶结点,所以pop这个结点出来就是中结点,然后指针继续遍历这个中结点的右孩子,如果存在右孩子,那么入栈,然后pop出来,如果不存在右孩子,那直接将栈中的栈顶元素再pop出来(因为现在没有右孩子了,就属于左中右已经遍历结束了,需要向父结点继续遍历了,此时的父结点就是栈顶元素,画个图就明白了)。
需要注意一个问题:在这里设置的指针是遍历结点的,并不是只遍历左孩子结点!!!!右孩子也管!!!!。
错误第一版:最后的输出结果都是[],也就是说res.append完全没用!推断错误原因在判断右孩子那里,在else判断中我设置了node来存储stack的栈顶元素然后将其加入到res中,接下来使用的却是cur的right,但是此时cur是什么???在本题中我们一直使用的是cur来判断当前遍历的结点,所以在else中也应该是用cur来存储现在要弹出的是哪个结点,然后判断的是这个结点之后的right。
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
cur = root
if root == None:
return res
while cur != None and len(stack) != 0:
if cur != None:
stack.append(cur)
cur = cur.left
else:
node = stack.pop()
res.append(node.val)
cur = cur.right
return res
错误第二版:输出结果和上面一样。发现问题在whilt循环中!!!while cur != None and len(stack) != 0:在这里我们用的是and len(stack) != 0但是在这句话之前并没有任何元素入栈,栈一直是空的,所以while循环根本没用上。
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
cur = root
if root == None:
return res
while cur != None and len(stack) != 0:
if cur != None:
stack.append(cur)
cur = cur.left
else:
cur = stack.pop()
res.append(cur.val)
cur = cur.right
return res
正确代码:
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
res = []
stack = []
cur = root
if root == None:
return res
while cur != None or len(stack) != 0:
if cur != None:
stack.append(cur)
cur = cur.left
else:
cur = stack.pop()
res.append(cur.val)
cur = cur.right
return res
层序遍历
思路
之前都是栈来实现,这个层序是用队列来实现的,就是先将第一层的结点加入到队列中,然后弹出,弹出结点的左右孩子按顺序加入到队列中,然后继续弹出结点在加入对应结点的左右孩子结点。但是我们要输出的是一个二位数据,也就是说每一层一个数组,所以需要判断哪些是一层呢?所以需要知道每层有多少个元素。
错误第一版:输出结果长这样。分析错误地方在于根本没区分出来每一层,虽然数值对了,但是都是在一层。所以发现问题在while len(queue) != 0:用这句话作为每一层的判断是不对的,因为queue只有当所有结点都输出结束才会变成空的,也就是说它里面一直是存在结点的,而且不一定是哪一层的。
注意:队列是先进先出的,也就是说是在队列的最左侧弹出元素,popleft,但是popleft只能在collections.deque()中才能用。所以要加import collections,并且queue的初始化应该是queue = collections.deque()。
import collections
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
result = []
queue = collections.deque()
if root == None:
return result
queue.append(root)
while len(queue) != 0:
res = []
while len(queue):
node = queue.popleft()
res.append(node.val)
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
result.append(res)
return result
正确代码:
import collections
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
result = []
queue = collections.deque()
if root == None:
return result
queue.append(root)
while len(queue) != 0:
res = []
for _ in range(len(queue)):
node = queue.popleft()
res.append(node.val)
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
result.append(res)
return result
开始1打10了奥
107. 二叉树的层序遍历 II
思路
和前面的一样一样的,最后的result反转一下就行。
import collections
class Solution:
def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
result = []
queue = collections.deque()
if root == None:
return result
queue.append(root)
while len(queue) != 0:
res = []
for _ in range(len(queue)):
node = queue.popleft()
res.append(node.val)
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
result.append(res)
return result[::-1]
199. 二叉树的右视图
import collections
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
result = []
queue = collections.deque()
if root == None:
return result
queue.append(root)
while len(queue) != 0:
res = []
for _ in range(len(queue)):
node = queue.popleft()
res.append(node.val)
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
result.append(res[-1])
return result
637. 二叉树的层平均值
import collections
class Solution:
def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
result = []
queue = collections.deque()
if root == None:
return result
queue.append(root)
while len(queue) != 0:
res = []
for _ in range(len(queue)):
node = queue.popleft()
res.append(node.val)
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
average = sum(res) / len(res)
result.append(average)
return result
429. N 叉树的层序遍历
错误第一版:这是一个N叉树,所以加入queue队列的就不是只有一个左孩子或者又孩子。所以在判断这里的时候if node.children != None:,应该是queue.extend(node.children)
注意:append和extend区别:queue.append(item): 这个方法用于将单个元素 item 添加到队列的右侧(末尾)。例如,queue.append(10) 将会将元素 10 添加到队列的末尾。
queue.extend(iterable): 这个方法用于将可迭代对象 iterable 中的元素逐个添加到队列的右侧(末尾)。例如,queue.extend([20, 30, 40]) 将会将列表 [20, 30, 40] 中的元素逐个添加到队列的末尾。
import collections
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
result = []
queue = collections.deque()
if root == None:
return result
queue.append(root)
while len(queue) != 0:
res = []
for _ in range(len(queue)):
node = queue.popleft()
res.append(node.val)
if node.children != None:
queue.append(node.children)
result.append(res)
return result
正确代码:
import collections
class Solution:
def levelOrder(self, root: 'Node') -> List[List[int]]:
result = []
queue = collections.deque()
if root == None:
return result
queue.append(root)
while len(queue) != 0:
res = []
for _ in range(len(queue)):
node = queue.popleft()
res.append(node.val)
if node.children != None:
queue.extend(node.children)
result.append(res)
return result
515. 在每个树行中找最大值
import collections
class Solution:
def largestValues(self, root: Optional[TreeNode]) -> List[int]:
result = []
queue = collections.deque()
if root == None:
return result
queue.append(root)
while len(queue) != 0:
res = []
for _ in range(len(queue)):
node = queue.popleft()
res.append(node.val)
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
maxnum = max(res)
result.append(maxnum)
return result
116. 填充每个节点的下一个右侧节点指针
思路
乍一看我没明白这题让干啥,仔细一看其实就是给每个结点加一个self.next = next指向其右结点,所以其实也是层序遍历,只是在遍历到每个结点的时候对他的self.next进行操作,指向它的下一个结点。所以需要设置一个pre结点初始化为None,因为要初始化为每一层第一个结点的前面。
错误第一版:错误原因是没有判断pre是不是None,如果是空是没有next的。
import collections
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
queue = collections.deque()
if root == None:
return root
queue.append(root)
while len(queue) != 0:
pre = None
for _ in range(len(queue)):
node = queue.popleft()
pre.next = node
pre = node
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
return root
正确代码:
import collections
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
queue = collections.deque()
if root == None:
return root
queue.append(root)
while len(queue) != 0:
pre = None
for _ in range(len(queue)):
node = queue.popleft()
if pre:
pre.next = node
pre = node
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
return root
117. 填充每个节点的下一个右侧节点指针 II
正确代码:(其实和上面一样)
import collections
class Solution:
def connect(self, root: 'Node') -> 'Node':
queue = collections.deque()
if root == None:
return root
queue.append(root)
while len(queue):
pre = None
for _ in range(len(queue)):
node = queue.popleft()
if pre:
pre.next = node
pre = node
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
return root
104. 二叉树的最大深度
import collections
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
queue = collections.deque()
result = []
if root == None:
return 0
queue.append(root)
while len(queue) != 0:
res = []
for _ in range(len(queue)):
node = queue.popleft()
res.append(node.val)
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
result.append(res)
return len(result)
111. 二叉树的最小深度
思路
其实最开始想的是深度遍历,但是既然这个题出现在层序遍历中,那么一定用层序能解决。
终止条件是当某一结点的左右孩子都为空的时候,就是最小深度了。
import collections
class Solution:
def minDepth(self, root: Optional[TreeNode]) -> int:
depth = 0
queue = collections.deque()
if root == None:
return 0
queue.append(root)
while len(queue) != 0:
depth += 1
for _ in range(len(queue)):
node = queue.popleft()
if node.left == None and node.right == None:
return depth
if node.left != None:
queue.append(node.left)
if node.right != None:
queue.append(node.right)
return depth