94. 二叉树的中序遍历
二刷
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> Result;
stack<TreeNode *> Stack;
while (root || !Stack.empty()) {
while (root) {
Stack.push(root);
root = root->left;
}
root = Stack.top();
Stack.pop();
Result.push_back(root->val);
root = root->right;
}
return Result;
}
};
说实话这个迭代真不好想吧,感觉纯粹就是凑出来的,丑得要命,只能硬记了,而且说不定还没什么大用。还是递归好。
三刷
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> Result;
stack<TreeNode *> S;
// These two while conditions correspond to:
// if (!root)
// return;
// And also the IMPLICIT return statement at the end of the function.
while (!S.empty() || root) {
while (root) {
// Traverse(root->left);
S.push(root);
root = root->left;
}
// Visit(root); Note that this is basically a BACKTRACK.
root = S.top();
S.pop();
Result.push_back(root->val);
// Traverse(root->right);
root = root->right;
}
return Result;
}
};
其实也不难理解。这两层 while 循环共同构成了两个不动点——一个是递归方法下函数开头的判空,另一个是访问完子节点之后的退栈。理解了这一点,就可以理解为什么我们要用这么别扭的写法了。
此外,也可以根据上面注释里给出的提示,对迭代法的代码进行对比记忆。
四刷
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> Ret;
stack<TreeNode *> S;
// The predicative looks like this because the root IS NOT PRESUMABLY NON-
// NULL, thus we need to perform a double-check: one for the root, another
// for the stack.
while (root != nullptr || !S.empty()) {
// The fixed point in every tree traversal algorithm is always `!root`,
// instead of `!root->left` or `!root->right`.
while (root != nullptr) {
S.push(root);
root = root->left;
}
// else {
// After the while loop above, root will always be nullptr; so we shall
// get the root IN THE PREVIOUS LAYER through the stack.
root = S.top();
Ret.emplace_back(root->val);
S.pop();
// Now we're safe to traverse the right part.
root = root->right;
// }
}
return Ret;
}
};
记住树遍历的不动点永远是root == nullptr而不是root->left == nullptr && root->right == nullptr之后,这题的S.push(root);就容易理解很多——push进去的东西永远是符合不动点规则的。
然后你也可以接着理解为什么我们的两层while循环的谓词都是root != nullptr,这也是受到不动点的影响。
此外,还要注意每个while (pred) { body; }后面都有一个else { body; },这样就能看清楚很多隐含条件。