树大致有四种遍历方式:前序遍历,中序遍历,后序遍历,层次遍历
一、前序遍历
遍历的顺序是 根->左孩子->右孩子
递归方式
void preOrder(TreeNode* root) {
if (!root) {
return;
}
cout << root->val;
preOrder(root->left);
preOrder(root->right);
}
非递归方式
非递归方式使用栈来维护。另外要设置一个辅助的指正p(我觉得这个辅助指针很重要)当栈不空或者p!=NULL时,进入循环。
如果p!=NULL,就将该结点压栈,因为是前序遍历,所以加入答案中。
如果p==NULL,说明当前遍历的最小子树的左子树遍历完成(此时当前最小子树已经完成了根,左孩子的遍历),p指向栈顶结点的右孩子,出栈。接着下一次的循环判断。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ans;
stack<TreeNode *> st;
TreeNode *p = root;
while(!st.empty() || p){
while(p){
st.emplace(p);
ans.push_back(p->val);
p = p->left;
}
p = st.top()->right;
st.pop();
}
return ans;
}
};
总的来说这个辅助指针p很重要就像是一个游标一样。
二、后序遍历
后序遍历的顺序是 左孩子->右孩子->根
递归方式
void postOrder(TreeNode* root) {
if (!root) {
return;
}
postOrder(root->left);
postOrder(root->right);
cout << root->val;
}
非递归方式
还是利用栈。做法非常巧妙。考虑前序遍历的顺序是根->左孩子->右孩子,而后序是 左孩子->右孩子->根,我们在前序的代码上稍作改动。
我们还是进行前序遍历,只不过前序遍历的顺序变成了根->右孩子->左孩子,但是现在每次放入答案数组的方式确实放在数组的开头而不是以前放在数组结尾,这样做的最后顺序和后序遍历一样。
代码是leetcode原题 https://leetcode.com/problems/binary-tree-postorder-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> postorderTraversal(TreeNode* root) {
stack<TreeNode *> s;
vector<int> ans;
TreeNode *p=root;
while(!s.empty()||p!=NULL){
if(p!=NULL){
ans.insert(ans.begin(),p->val);
s.push(p);
p=p->right;
}
else{
p=s.top()->left;
s.pop();
}
}
return ans;
}
};
三、中序遍历
递归方式
中序遍历的顺序是左孩子->根->右孩子
void inOrder(TreeNode* root) {
if (!root) {
return;
}
inOrder(root->left);
cout << root->val;
inOrder(root->right);
}
非递归
还是用栈。和前序思路一样,p为辅助指针,当栈不空或者p!=NULL时循环。
遍历的题改变的就是结点入答案数组的这局代码的位置。中序遍历肯定不能再一开始碰到这个结点就入,应当在p==NULL时,说明当前遍历的这棵最小树的左子树遍历完了,所以就到了根结点。注意辅助指针p的改变。
代码是leetcode原题的代码https://leetcode.com/problems/binary-tree-inorder-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> ans;
stack<TreeNode *> st;
TreeNode *p = root;
while(!st.empty() || p){
while(p){
st.emplace(p);
p = p->left;
}
ans.push_back(st.top()->val);
p = st.top()->right;
st.pop();
}
return ans;
}
};
四、层次遍历
利用队列先进先出的原则,当队列不空的时候进行循环。mid数组存放每一层的结点
设置两个计数变量,pre用来计算上一层有多少个结点,cur计算当前层加入多少结点。pre赋初值1,cur=0;
小循环内,将上一层的结点全部遍历一遍,自然循环终止的条件 就是pre==0,对于上一层每一个结点现将其左右孩子进队,不要忘了cur++,然后将该结点加入mid数组中,相当于遍历了这个结点。弹出后pre--;
当上一层结点全部遍历完后,将pre和cur进行重置,将这一层的结点数组mid放入答案数组ans中。
代码是 leetcode原题的AC代码:https://leetcode.com/problems/binary-tree-level-order-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<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans;
if(root==NULL)return ans;
vector<int> mid;
queue<TreeNode*> t;
t.push(root);
int pre=1,cur=0;
TreeNode* p;
while(!t.empty()){
while(pre!=0){
p=t.front();
if(p->left!=NULL){t.push(p->left);cur++;}
if(p->right!=NULL){t.push(p->right);cur++;}
mid.push_back(p->val);
t.pop();
pre--;
}
ans.push_back(mid);
mid.clear();
pre=cur;cur=0;
}
return ans;
}
};
树的遍历的题
https://leetcode.com/problems/binary-tree-zigzag-level-order-traversal/
https://leetcode.com/problems/binary-tree-level-order-traversal-ii/