首先谈谈树的三种遍历方式:
- 前序遍历:根左右
- 中序遍历:左根右
- 后序遍历:左右根
关于Leetcode Binary Tree Inorder Traversal,这里讨论三种遍历方式:
- 递归遍历 递归理论上也是O(n)的空间,因为递归需要用到栈
- 迭代遍历 迭代需要用到队列来保存状态,空间复杂度为O(n)
- Morris Traversal 此种方法特为巧妙,有点类似加入线索二叉树(threaded binary tree)的概念,空间复杂度O(1)
版本1:递归
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
void inorder(TreeNode* root, vector<int>& vec) {
if(root->left != NULL) inorder(root->left, vec); // 左
vec.push_back(root->val); // 根
if(root->right != NULL) inorder(root->right, vec); // 右
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> vec;
if(root == NULL) return vec;
inorder(root, vec);
return vec;
}
};
版本2: 迭代
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> vec;
if(root == NULL) return vec;
stack<TreeNode*> st;
TreeNode *cur = root;
while(!st.empty() || cur != NULL) {
while(cur != NULL) { //不断先将左子树节点加入栈
st.push(cur);
cur = cur->left;
}
cur = st.top();
st.pop();
vec.push_back(cur->val);
cur = cur->right;
}
return vec;
}
};
版本3. Morris Traversal
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> vec;
if(root == NULL) return vec;
TreeNode* cur = root;
TreeNode* prev = NULL;
while(cur) {
// 如果当前左孩子为空,则其直接输出,并遍历设立右孩子为遍历节点
if(cur->left == NULL) {
vec.push_back(cur->val);
cur = cur->right;
continue;
}
// 如果左孩子不为空
prev = cur->left;
// 找到左孩子中的最右节点,也就是左子树值最大的节点
while(prev->right != NULL && prev->right != cur) prev = prev->right;
// 如果左子树值最大节点的右孩子为空,那么就将其指向当前cur,这样在下次遍历到
// 此节点时,我们可以直接回溯到比左子树最大节点的下一个节点,也就是cur
if(prev->right == NULL) {
prev->right = cur;
cur = cur->left;
}
// 如果当前已经指向了最右节点,那么此时说明已经遍历到了此节点,要还原二叉树
// 因为原本指向的right的为NULL,并将cur回溯到原本的左子树位置。
else {
prev->right = NULL;
vec.push_back(cur->val);
cur = cur->right;
}
}
return vec;
}
};