二叉树的遍历分为以下几种:
1. 层次遍历
2. 前序遍历
3. 中序遍历
4. 后序遍历
其中分为两类:层次遍历单作为一类,其他三种方式为另外一类。
先总结前中后序这三种方法,如果有朋友恰好看到我的文章来找思路,我建议先别看代码的实现,人看了别人的代码自然而然地会有先入为主的感觉,说的不好听一点,叫背别人的代码,这是下意识地行为,所以还是一开始先别看为好。不知道我能不能讲清楚二叉树的道理,本人能力有限,抱歉。
所以在这里推荐大家去看看leetcode,在探索栏目中有二叉树这个模块,感觉很不错
首先大家想到的是递归的方式,这也是我们最多看到的。我们怎么想出递归这个思路呢?
前中后序三种方法的特点在于单独看树的每个一小部分,它们都是一样的。我们把其他部分先不看,每个部分都是这样 一个自己本身,两个左右的子节点(子节点也可能不存在,这是边界情况)。将树拆分成一个一个这样的小块,最后将没有子节点的边界条件处理掉,这就是递归方法的主要思路。
循环的方法主要思路在于使用一个容器来模拟递归。
前序遍历:
遍历首先访问根节点,然后遍历左子树,最后遍历右子树。
递归版本:
/**
* 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;
traversal(root, res);
return res;
}
void traversal(TreeNode * root, vector<int> &res)
{
if(!root) return;
traversal(root->left, res);
res.push_back(root->val);
traversal(root->right, res);
}
};
循环版本:
/**
* 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) {
treeNodes.push_back(root);
while(treeNodes.size()) {
TreeNode* tar = treeNodes.back();
treeNodes.pop_back();
if(!tar) continue;
ret.push_back(tar->val);
treeNodes.push_back(tar->right);
treeNodes.push_back(tar->left);
}
return ret;
}
private:
vector<int> ret;
vector<TreeNode*> treeNodes;
};
中序遍历:
中序遍历是先遍历左子树,然后访问根节点,然后遍历右子树。
循环版本:
/**
* 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) {
tre.push(root);
while(tre.size()) {
TreeNode* top = tre.top();
tre.pop();
if(!top) continue;
if(top->right || top->left) {
tre.push(top->right);
TreeNode* newOne = new TreeNode(top->val);
tre.push(newOne);
tre.push(top->left);
} else {
vec.push_back(top->val);
}
}
return vec;
}
private:
vector<int> vec;
stack<TreeNode*> tre;
};
中后序遍历的循环版本和前序有所不同,在前序版本中,我们对于节点自身的值的处理是立即执行的,而在中后序中对于节点自身的处理是在处理过其他节点之后的,所以在这里我们构建一个没有子节点的新节点(边界情况)在存储节点本身的值。
当然你也可以直接将节点本身的两个子节点指针设为NULL,然后置入stack,这样就不用创建新节点了,不过这样就破坏了原来的二叉树。
递归版本:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
traversal(root, res);
return res;
}
void traversal(TreeNode * root, vector<int> &res)
{
if(!root) return;
traversal(root->left, res);
res.push_back(root->val);
traversal(root->right, res);
}
};
后序遍历:
与前中序雷同,也就不在赘述。
层次遍历:
层次遍历的思路有所不同:层序遍历是逐层遍历树结构。
在层次遍历中,我们选择队列而不是栈,因为栈在操作中我们会不断地压入新的节点,无法实现每次将一层的节点处理完,再处理下一层节点的需求。
我选择创建tre, child_tre两个队列来存储节点,tre是我要处理的当前层次的节点,child_tre是tre中节点的子节点们。在每层处理完成后,我将交换child_tre和tre的值,如此循环,直到最后一次处理完时child_tre为空,交换后tre也就为空,然后退出while循环。
/**
* 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) {
tre.push(root);
TreeNode* tar;
vector<vector<int>> vec;
if(!root) return vec;
while(tre.size()){
vec.push_back(vector<int>());
while(tre.size()) {
tar = tre.front();
tre.pop();
if(!tar) continue;
vec.back().push_back(tar->val);
if(tar->left)
child_tre.push(tar->left);
if(tar->right)
child_tre.push(tar->right);
}
tre.swap(child_tre);
}
return vec;
}
private:
queue<TreeNode*> tre, child_tre;
};