94、二叉树的中序遍历
思路
- 显式的维护一个栈,在栈中按照"右中左"的顺序把节点压入栈中。
- 没法找到一次性把所有节点按照顺序压入栈中的终止条件。
- 换一种方法。把节点按顺序压入栈中,同时把要添加进结果集的节点添加标记。
- 使用标记进行条件判断。遇到标记,表示遇到了要添加进结果集的节点;没有遇到标记时,则继续遍历二叉树,把节点按顺序压入栈中。
代码
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
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();
res.push_back(node -> val);
}
}
return res;
}
};
144、二叉树的前序遍历
思路
- 对比中序遍历,只是修改了把节点压入栈的顺序
- 仍然需要给要加入结果集的元素添加标记
代码
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> res;
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();
res.push_back(node -> val);
}
}
return res;
}
};
145、二叉树的后序遍历
思路
- 相比于中序遍历,也只是改变里把节点压入栈的顺序。
- 压入栈的顺序不同也是因为三种遍历方式不同。
代码
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> res;
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();
res.push_back(node -> val);
}
}
return res;
}
};
思考
- 在每轮遍历中,要给哪个节点添加标记?
- 给节点添加标记的目的是确定下来这个节点在加入结果集时的顺序。
- 经过模拟得出,每轮遍历应该给中间节点添加标记,来确定正确X序遍历的顺序。
- 因为三种遍历顺序的不同,将节点压入栈中的顺序也不同,受到标记的中间节点不一定会在被标记之后紧接着被添加进结果集,而是先被确定了加入结果集的顺序。
- 添加标记的作用
- 将栈中的节点加入结果集的顺序确定下来。
- 利用标记可以进行条件判断,从而可以把被标记的节点添加进结果集。