LeetCode - 94. 二叉树的中序遍历
题目
94. 二叉树的中序遍历 - 力扣(LeetCode)
什么是中序遍历
二叉树的中序遍历是按照"左-根-右"的顺序访问二叉树中的所有节点。
具体过程:
- 先遍历左子树(递归)
- 然后访问根节点
- 最后遍历右子树(递归)
例如,对于下面的二叉树:
1/ \2 3/ \ \
4 5 6
中序遍历的结果是:[4, 2, 5, 1, 3, 6]
遍历步骤:
- 从根节点开始,先访问左子树
- 访问节点4(左叶子)
- 访问节点2(4的父节点)
- 访问节点5(2的右子节点)
- 访问节点1(根节点)
- 访问节点3(右子节点)
- 访问节点6(3的右子节点)
递归写法
读者可能会出现的错误写法
class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> result; dfs(root,result);return result;}void dfs(TreeNode* root,vector<int> result){if(!root){return;}dfs(root->left,result);result.push_back(root->val);dfs(root->right,result);}
};
在dfs函数中,result参数是按值传递的,而不是按引用传递的,这会导致对result的修改不会保存。
每次调用dfs函数时:
- 会创建result的一个新副本
- 当执行result.push_back(root->val)时,只是向这个副本中添加元素
- 递归调用子节点的dfs时,又会创建新的副本
- 当函数返回时,这个副本被销毁,其中存储的所有节点值都消失了
正确写法
class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> result; dfs(root,result);return result;}void dfs(TreeNode* root,vector<int>& result){if(!root){return;}dfs(root->left,result);result.push_back(root->val);dfs(root->right,result);}
};
非递归写法
思路
- 沿着左子树一直深入,将所有节点压入栈中
- 当无法继续向左时,弹出栈顶节点,访问它
- 然后处理该节点的右子树
具体实现步骤:
- 创建一个空栈和一个指向当前节点的指针curr(初始为根节点)
- 当curr不为空或栈不为空时循环:
- 如果curr不为空,将curr压入栈,然后curr移动到其左子节点
- 如果curr为空,弹出栈顶节点,将其值加入结果数组,然后curr移动到其右子节点
读者可能的错误写法
class Solution {
public:vector<int> inorderTraversal(TreeNode* root) {vector<int> result;stack<TreeNode*>st;TreeNode* node = root;while(node || !st.empty()){while(node){st.push(node);node=node->left;}//将栈顶的元素放入数组中result.push_back(node->val);st.pop();node = node->right;}return result;}
};
在尝试访问node->val之前,node已经是nullptr。在内层while循环结束后,node已经是nullptr,因为循环的终止条件就是node为空。因此,在这之后直接访问node->val和node->right会导致空指针解引用错误。
正确的方法
vector<int> inorderTraversal(TreeNode* root)
{vector<int> result;stack<TreeNode*> st;TreeNode* node = root;while(node || !st.empty()){while(node){st.push(node);node = node->left;}node = st.top(); // 先获取栈顶节点st.pop();result.push_back(node->val);node = node->right;}return result;
}