三种方法处理二叉树的前序、中序、后序遍历
题目链接:144 二叉树的前序遍历
递归遍历
思考:考虑好递归函数的参数和返回值,终止条件以及单层递归的逻辑,三种遍历的差别仅单层递归的代码顺序
代码(前序遍历):
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == nullptr) return;
vec.push_back(cur->val);
traversal(cur->left, vec);
traversal(cur->right, vec);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
代码(后序遍历):
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == nullptr) return;
traversal(cur->left, vec);
traversal(cur->right, vec);
vec.push_back(cur->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
代码(中序遍历):
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == nullptr) return;
traversal(cur->left, vec);
vec.push_back(cur->val);
traversal(cur->right, vec);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
迭代遍历
前序遍历
思考: 先将根节点入栈,然后将右孩子入栈,再将左孩子入栈,这样出栈的时候才是中左右的顺序。每次处理中出栈当前节点并将val值写入数组result,再将非空的右孩子和左孩子入栈,直到栈空。
代码:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root == nullptr) return result;
st.push(root); //中
while (!st.empty()) {
TreeNode* cur = st.top();
result.push_back(cur->val);
st.pop();
if (cur->right != nullptr) st.push(cur->right); //右
if (cur->left != nullptr) st.push(cur->left); //左
}
return result;
}
};
后序遍历
思考: 只需要将前序遍历的左右节点入栈顺序改变,可得到存储顺序中右左,再将数组反转一下
代码:
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root == nullptr) return result;
st.push(root); //中
while (!st.empty()) {
TreeNode* cur = st.top();
result.push_back(cur->val);
st.pop();
if (cur->left != nullptr) st.push(cur->left); //左
if (cur->right != nullptr) st.push(cur->right); //右
}
reverse(result.begin(),result.end()); //反转数组,将中右左变成左右中
return result;
}
};
中序遍历
思考: 先访问二叉树根节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点。每次循环一直向左下寻空节点并将非空节点入栈直到当前节点为空,此时让当前节点指向栈顶元素(左孩子为空或左孩子已经访问过的节点),出栈当前节点并将val值写入数组result,再将当前节点指向右孩子,直到栈空。
代码:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
TreeNode* cur = root;
while(cur != nullptr || !st.empty()) {
if (cur != nullptr) {
st.push(cur);
cur = cur->left; //左 指针访问到最左下
} else {
cur = st.top(); //栈顶存放着左孩子为空或者左孩子已经访问过的节点
st.pop();
result.push_back(cur->val); //中
cur = cur->right; //右
}
}
return result;
}
};
统一迭代法
思考:上面的迭代法无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况。那就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。标记法:要处理的节点放入栈之后,紧接着放入一个空指针作为标记。
代码(前序遍历):
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == nullptr) return result;
st.push(root);
while (!st.empty()) {
TreeNode* cur = st.top();
if (cur != nullptr) {
st.pop();
if (cur->right != nullptr) st.push(cur->right); //右
if (cur->left != nullptr) st.push(cur->left); //左
st.push(cur); //中
st.push(nullptr);//null标志 说明后面的节点访问过
} else { //遇到null统一输出栈中后一个元素
st.pop();
cur = st.top();
st.pop();
result.push_back(cur->val);
}
}
return result;
}
};
代码(后序遍历):
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == nullptr) return result;
st.push(root);
while (!st.empty()) {
TreeNode* cur = st.top();
if (cur != nullptr) {
st.pop();
st.push(cur); //中
st.push(nullptr);//null标志 说明后面的节点访问过
if (cur->right != nullptr) st.push(cur->right); //右
if (cur->left != nullptr) st.push(cur->left); //左
} else { //遇到null统一输出栈中后一个元素
st.pop();
cur = st.top();
st.pop();
result.push_back(cur->val);
}
}
return result;
}
};
代码(中序遍历):
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == nullptr) return result;
st.push(root);
while (!st.empty()) {
TreeNode* cur = st.top();
if (cur != nullptr) {
st.pop();
if (cur->right != nullptr) st.push(cur->right); //右
st.push(cur); //中
st.push(nullptr);//null标志 说明后面的节点访问过
if (cur->left != nullptr) st.push(cur->left); //左
} else { //遇到null统一输出栈中后一个元素
st.pop();
cur = st.top();
st.pop();
result.push_back(cur->val);
}
}
return result;
}
};
自我总结
了解标记法,通过设置指定的标志标记要处理的元素,统一化遍历和处理