1、前序遍历
递归
void PreOrder(TreeNode* root) {
if (root == nullptr) {
return;
}
int val = root->val; // 访问
PreOrder(root->left);
PreOrder(root->right);
}
非递归
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ret;
if (root == nullptr) {
return ret;
}
stack<TreeNode*> st;
TreeNode *p = root;
while(!st.empty() || p != nullptr) {
while(p != nullptr) {
ret.push_back(p->val);
st.push(p);
p = p->left;
}
p = st.top();
st.pop();
p = p->right;
}
return ret;
}
};
用一个栈保存经过的节点,再出栈往右边走。
2、中序遍历
非递归
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
if (root == nullptr) {
return ret;
}
stack<TreeNode*> st;
TreeNode* p = root;
while(!st.empty() || p != nullptr) {
while(p != nullptr) {
st.push(p);
p = p->left;
}
p = st.top();
st.pop();
ret.push_back(p->val);
p = p->right;
}
return ret;
}
};
3、后序遍历
非递归
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ret;
if (root == nullptr) {
return ret;
}
TreeNode* p = root;
TreeNode* pre = nullptr;
stack<TreeNode*> st;
while(!st.empty() || p != nullptr) {
while(p != nullptr) {
st.emplace(p);
p = p->left;
}
p = st.top();
st.pop();
if (p->right == nullptr || p->right == pre) {
ret.emplace_back(p->val);
pre = p;
p = nullptr;
} else {
st.emplace(p);
p = p->right;
}
}
return ret;
}
};
非递归算法看起来其实都差不多,都是利用栈,先找到最左边的节点,把路上的节点不断保存(放入栈内),到了叶子节点就出栈,然后往右边走一步。
前序在路上就访问节点
中序在出栈后访问节点(左边已经访问过了)
后序有点不一样,要一个pre标记右边是否被访问过,如果访问过,就访问当前节点,并改变pre=p,p=nullptr(p已经出栈并且已经访问完,需要重置)。如果没访问过,当前节点入栈,向右边走。后面的循环出栈时再遇到这个节点就能根据pre判断。
4、层次遍历
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ret;
if (root == nullptr) {
return ret;
}
deque<TreeNode*> a;
a.push_back(root);
while(a.size() != 0) {
vector<int> tmp;
int sz = a.size();
for (int i = 0; i < sz; ++i) {
TreeNode *q = a.front();
a.pop_front();
tmp.emplace_back(q->val);
if (q->left != nullptr) {
a.push_back(q->left);
}
if (q->right != nullptr) {
a.push_back(q->right);
}
}
ret.emplace_back(tmp);
}
return ret;
}
};
主要使用一个队列,每次循环把每层的节点依次入栈,创建一个临时数组tmp用来保存一层的节点。
锯齿形层次遍历:力扣
class Solution {
public:
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> ret;
deque<TreeNode*> dq;
if (root == nullptr) {
return ret;
}
dq.push_back(root);
int cs = 1;
while(dq.size() != 0) {
deque<int> tmp;
int sz = dq.size();
for (int i = 0; i < sz; ++i) {
TreeNode* q = dq.front();
dq.pop_front();
if (cs % 2 == 1) {
tmp.push_back(q->val);
} else {
tmp.push_front(q->val);
}
if (q->left != nullptr) {
dq.push_back(q->left);
}
if (q->right != nullptr) {
dq.push_back(q->right);
}
}
cs = cs + 1;
ret.push_back(vector<int>{tmp.begin(), tmp.end()});
}
return ret;
}
};
判断层数然后正着放还是倒着放即可。
5、Morris
前序遍历
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ret;
if (root == nullptr) {
return ret;
}
TreeNode* pre = nullptr;
TreeNode* p = root;
while(p != nullptr) {
if (p->left != nullptr) {
pre = p->left;
while(pre->right != nullptr && pre->right != p) {
pre = pre->right;
}
if (pre->right == nullptr) {
ret.push_back(p->val);
pre->right = p;
p = p->left;
} else {
pre->right = nullptr;
p = p->right;
}
} else {
ret.push_back(p->val);
p = p->right;
}
}
return ret;
}
};
中序遍历
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
if (root == nullptr) {
return ret;
}
TreeNode* pre = nullptr;
TreeNode* p = root;
while(p != nullptr) {
if (p->left != nullptr) {
pre = p->left;
while(pre->right != nullptr && pre->right != p) {
pre = pre->right;
}
if (pre->right == nullptr) {
pre->right = p;
p = p->left;
} else {
ret.push_back(p->val);
pre->right = nullptr;
p = p->right;
}
} else {
ret.push_back(p->val);
p = p->right;
}
}
return ret;
}
};
后序遍历
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ret;
if (root == nullptr) {
return ret;
}
TreeNode* p = root;
TreeNode* pre = nullptr;
while(p != nullptr) {
if (p->right != nullptr) {
pre = p->right;
while(pre->left != nullptr && pre->left != p) {
pre = pre->left;
}
if (pre->left == nullptr) {
ret.push_back(p->val);
pre->left = p;
p = p->right;
} else {
pre->left = nullptr;
p = p->left;
}
} else {
ret.push_back(p->val);
p = p->left;
}
}
reverse(ret.begin(), ret.end());
return ret;
}
};
Morris就是找到左子树的最右节点并把最右节点和当前节点连接起来,这样就做到了遍历时保存的作用。
具体就是每次循环先找到左子树中的最右节点,根据其右孩子为nullptr或者p,判断是否已经连接,如果是前序遍历,未连接时直接进行访问,并让p向左走,pre连接p,如果是中序遍历,已连接时(代表左边已经访问过)直接进行访问,并重置pre,p向右走。如果不存在左子树,那均直接访问,并向右走。
后序遍历因为是左、右、中的顺序,可以倒过来中、右、左,把前序遍历修改最后再翻转答案即可。