二叉树的遍历总结
二叉树是一种非常常见的数据结构,对图的遍历我们一般有深度优先遍历和广度优先遍历两种,而树是一种特殊的图,即无环图。所以我们对树的遍历一般也采用以上两种方法,特殊的,对于二叉树,我们习惯地将深度优先遍历分为中序遍历、前序遍历和后序遍历三种,而层次遍历则为广度优先遍历。
中序遍历
中序遍历过程的顺序是左 -> 根 -> 右
递归算法
递归算法比较简单,就根据中序遍历的过程,先遍历左子树,再遍历当前根的值,然后遍历右子树。当遍历到根结点时,将该点加入遍历数组即可。递归函数最重要的是中止条件,当遍历当前结点为空时,即可返回。
调用递归函数时可以看作它已经帮你完成了你所想要完成的所有事,在递归函数结束后,它就已经将答案封装好交到了你的手上。
/**
* 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> res;
vector<int> inorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
void dfs(TreeNode* root)
{
if (!root) return;
dfs(root -> left);
res.push_back(root -> val);
dfs(root -> right);
}
};
迭代算法
递归函数实现的过程其实就是系统帮我们调用栈的过程,所以如果换为迭代算法来写,我们只需要自己模拟实现一个栈。
递归的实现也可以反映成一棵树,就是所谓的递归树,当我们调用递归函数的时候,其实有两个过程,一个是向下的递
的过程,然后再是向上的归
的过程。递归函数很好地封装了这一点,然而当我们用迭代算法时,需要自己去模拟递
和归
的过程。
遍历是,我们需要将所有左子树链上的所有点放入栈中,该过程即为递
,然后取出栈顶,加入遍历数组,之后再放入右子树,同时注意这里的右子树指的是整个右子树的左子树链。直到遍历完所有结点。
/**
* 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> res;
stack<TreeNode*> st;
auto p = root;
while (p || !st.empty())
{
while (p) //将二叉树左链条全部入栈
{
st.push(p);
p = p -> left;
}
p = st.top(); //左链条全部进栈或栈不为空时,弹出并遍历
st.pop();
res.push_back(p -> val);
p = p -> right;
}
return res;
}
};
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> res;
vector<int> preorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
void dfs(TreeNode* root)
{
if (!root) return;
res.push_back(root -> val);
dfs(root -> left);
dfs(root -> right);
}
};
迭代算法
当前遍历结点即为根
,所以在迭代算法中,先将该点放入遍历数组,然后将左子树链递
到最低层,然后出栈,放入右子树。
/**
* 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> preorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> st;
auto p = root;
while (p || !st.empty())
{
while (p)
{
res.push_back(p -> val);
st.push(p);
p = p -> left;
}
p = st.top();
st.pop();
p = p-> right;
}
return res;
}
};
3. 后序遍历
后序遍历的顺序为左 -> 右 -> 根
根据观察,后序遍历倒过来的顺序为根 -> 右 -> 左
而我们已经知道如何进行先序遍历根 -> 左 -> 右
在递归算法中比较好理解,只需要调整遍历顺序。但是在迭代算法中,我们首先需要先模拟先序遍历
的过程,同时将先序遍历中左右子树
遍历过程交换
,最后还需将遍历数组翻转
才可以得到后序遍历。
递归算法
/**
* 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> res;
vector<int> postorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
void dfs(TreeNode* root)
{
if (!root) return;
dfs(root -> left);
dfs(root -> right);
res.push_back(root -> val);
}
};
迭代算法
/**
* 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) {
vector<int> res;
stack<TreeNode*> st;
auto p = root;
while (p || !st.empty())
{
while (p)
{
res.push_back(p -> val);
st.push(p);
p = p -> right;
}
p = st.top();
st.pop();
p = p -> left;
}
reverse(res.begin(), res.end());
return res;
}
};
4. 层次遍历
算法思路
层序遍历,顾名思义需要对二叉树进行一层一层的遍历。因此可以采用宽度优先搜索BFS
的思路,根据BFS
的性质,每进行一次扩展,会将下一层的点全部放入队列中,所以每次在对队列进行取出操作时,当前队列中的所有元素均当前层元素。因为我们需要一边取出元素,一边放入元素。所以在每一次更迭前,先记录当前的队列的大小,即为当前层的元素,然后取出队头的该几个元素,放入当前层遍历数组level
中,同时对左右儿子进行扩展。每遍历完一层,最后将level
数组插入最终遍历数组中。
C ++代码
/**
* 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>> res;
if (!root) return res;
queue<TreeNode*> q;
q.push(root);
while (q.size())
{
int n = q.size();
vector<int> level;
for (int i = 0; i < n; i ++)
{
auto t = q.front();
q.pop();
level.push_back(t -> val);
if (t -> left) q.push(t -> left);
if (t -> right) q.push(t -> right);
}
res.push_back(level);
}
return res;
}
};
相对于迭代算法来说,递归算法代码更简洁,在使用递归函数的时候我们不需要考虑计算机对栈的调用细节。在迭代算法里,需要用
while循环
来模拟实现递归函数递
的过程。
本文给出的二叉树遍历代码简洁易读,当理解完二叉树的遍历之后,大家可以试着写一写多叉树的遍历。
欢迎关注我的公众号Coderoger,原创文章第一时间推送。