Day14 二叉树part01
1、二叉树递归遍历
一、二叉树的前序、中序、后续遍历:
前序:中左右,中序:左中右,后续:左右中(分别对应中在哪个位置)
二、递归三要素:
- 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
- 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
- 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
三、应用于本题
1、确定递归函数参数和返回值:本题中需要打印最终的节点遍历顺序,所以需要传入需要被操作的节点cur(指针)and目前的遍历情况(列表)vector<int>
2、终止条件,如果cur为空则结束
3、确定单层逻辑:由于是中→前→后,所以先把中间节点push进来,然后递归找左边节点,再找右边节点
4、注意Tree的定义
144. 二叉树的前序遍历(递归)
# 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 preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
left = self.preorderTraversal(root.left)
right = self.preorderTraversal(root.right)
return [root.val]+left+right
145. 二叉树的后序遍历
# 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 postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
left = self.postorderTraversal(root.left)
right = self.postorderTraversal(root.right)
# print(root.val, 'left', left)
# print(root.val, 'right', right)
return left + right + [root.val]
94. 二叉树的中序遍历
# 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 inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
left = self.inorderTraversal(root.left)
right = self.inorderTraversal(root.right)
return left + [root.val] +right
2、二叉树非递归遍历-迭代遍历
用栈来模拟一个递归的过程
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
前序遍历伪代码:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st; //先构建一个栈
vector<int> result;// 结果数组
if (root == NULL) return result;
st.push(root); //初始化,先push第一个进去
while (!st.empty()) {
TreeNode* node = st.top();// 中,取出顶端元素
st.pop();
result.push_back(node->val); // 存入结果
if (node->right) st.push(node->right);// 右(空节点不入栈)
if (node->left) st.push(node->left); // 左(空节点不入栈)
}
return result;
}
};
后续遍历,需要对代码进行修改的地方:
前序遍历是“中左右”,调整左右那两行代码变为“中右左”,在最后结果result直接reverse变为“左右中”成为后续遍历。至于如何reverse除了用库函数,也可以想到数组交换
中序遍历:
代码-前序遍历非递归:
# 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 preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
st = [root]
res = []
cur = root
while st: #这里只看栈是不是空,不用管cur
cur = st.pop()
res.append(cur.val)
if cur.right: # 注意栈的顺序,因为想先左边所以要先把右边压入
st.append(cur.right)
if cur.left:
st.append(cur.left)
# elif not cur.left and not cur.right:
# cur = None
# print(st, cur, res)
return res
代码-后序遍历非递归:
class Solution:
def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
st = [root]
res = []
while st: #这里只看栈是不是空,不用管cur
cur = st.pop()
res.append(cur.val)
if cur.left:
st.append(cur.left)
if cur.right: # 注意栈的顺序
st.append(cur.right)
return res[::-1]
代码-中序遍历非递归:
首先记录自己的一个错误版本,一开始的思路是:当栈不为空时,如果存在左孩子就一直指下去。if指针指向的是none则pop出栈中的元素,并append到结果中,然后把指针指向这个元素;else 如果指针不是none,如果存在左孩子则左孩子推入stack,反之存在右孩子则有孩子推入stack
这个想法很复杂,简洁的想法是:如果指针有值,push进栈,指针指左;如果指针没有值,从栈里pop出的作为指针,res存储,指针指右;如果栈为空且指针为空则退出
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
st = []
res = []
cur = root
while cur or st:
if not cur:
cur = st.pop()
res.append(cur.val)
cur = cur.right
else:
st.append(cur)
cur = cur.left
# print(cur, st, res)
return res