一 144 二叉树的前序遍历
递归遍历二叉树就是用系统栈来保存上一层递归需要用到的参数,采用迭代法就是用我们自定义的栈模拟这个过程,需要注意的是入栈时先将右孩子入栈,再将左孩子入栈,这是为了保证出栈的顺序。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
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;
}
};
二 94 二叉树的中序遍历
栈里使我们访问过的元素,先一路向左加入栈中,直至空节点,弹出栈中元素,将其右结点加
将左孩子加入栈中,若左孩子为空,弹出栈中元素,访问该元素的左孩子,访问该元素,再将其右孩子加入栈中
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
//指针用于遍历各个结点,以便输出值
TreeNode* cur = root;
//栈用于保存访问过的结点,以便指针返回
stack<TreeNode*> st;
//cur为空且栈为空时整个遍历才结束
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;
}
};
三 145 二叉树的后序遍历
将前序遍历的中左右,改变下左右入栈的顺序,变成中右左,再将其反转就变成后序遍历的左右中了。
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
if (node->right) st.push(node->right); // 空节点不入栈
}
reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
return result;
}
};
四 二叉树的统一迭代
统一迭代的核心思想是,将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记,标记的方法就是就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 三种遍历方式只是相差了几行代码的顺序。
//中序遍历
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root != NULL) st.push(root);
//栈空则结束
while (!st.empty()) {
TreeNode* node = st.top();
//栈顶不是空节点
if (node != NULL) {
st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
if (node->right) st.push(node->right); // 添加右节点(空节点不入栈)
st.push(node); // 添加中节点
st.push(NULL); // 中节点访问过,但是还没有处理,加入空节点做为标记。
if (node->left) st.push(node->left); // 添加左节点(空节点不入栈)
} else { // 栈顶是空节点,此时将下一个节点放进结果集
st.pop(); // 空节点弹出
node = st.top(); // 重新取出栈中元素
st.pop();
result.push_back(node->val); // 加入到结果集
}
}
return result;
}
};
//前序遍历
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop();
if (node->right) st.push(node->right); // 右
if (node->left) st.push(node->left); // 左
st.push(node); // 中
st.push(NULL);
} else {
st.pop();
node = st.top();
st.pop();
result.push_back(node->val);
}
}
return result;
}
};
//后序遍历
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
if (node != NULL) {
st.pop();
st.push(node); // 中
st.push(NULL);
if (node->right) st.push(node->right); // 右
if (node->left) st.push(node->left); // 左
} else {
st.pop();
node = st.top();
st.pop();
result.push_back(node->val);
}
}
return result;
}
};
五 总结
- 二叉树递归需要注意三点:返回值和参数、终止条件、单层递归的处理逻辑
- 先序遍历的迭代法由于访问顺序和遍历输出的顺序相同,所以简单;中序由于两个顺序不同,因此需要指针进行遍历输出,栈用于存储访问过的节点。而统一迭代法采用空节点进行标记,处理逻辑类似,只是相差代码的顺序。