前序遍历
给定一个二叉树,返回它的 前序 遍历。
示例:
二叉树的遍历通常有两种方法,分别为递归法和迭代法,首先介绍递归法,该方法是实现最简单的,首先需要知道前序遍历的特点,先存储树的根节点然后遍历左子树并对左子树进行相应的操作,最后遍历右子树并对右子树进行相应的操作,采用深度优先搜索向下遍历,这里需要定义一个递归函数来遍历该树。
递归法
vector<int> preorderTraversal(TreeNode* root) {
vector<int>res;
helper(root,res); //定义一个辅助函数进行遍历
return res;
}
void helper(TreeNode *root,vector<int>&res){
if(root!=nullptr){
res.push_back(root->val); //首先将根节点存入到结果中
helper(root->left,res); //遍历左子树,并对左子树进行相应的操作
helper(root->right,res); //遍历右子树,并对右子树进行相应的操作
}
}
接下来介绍迭代法,每个递归法都可以转化为相应的迭代法,迭代法需要一个辅助栈来存储树的子节点,其进出栈过程如下
迭代法代码实现如下:
vector<int>preorderTraversal(TreeNode* root){
stack<TreeNode*>st;
vector<int>res;
if(root==nullptr)return res;
st.push(root); //首先将根节点入栈
while(!st.empty()){
TreeNode *first=st.top(); //将栈顶出栈
st.pop();
res.push_back(first->val); //将出栈元素加入到结果中
if(first->right!=nullptr){ //将栈顶的右子节点入栈
st.push(first->right);
}
if(first->left!=nullptr){ //将栈顶的左子节点入栈
st.push(first->left);
}
}
return res;
}
中序遍历
给定一个二叉树,返回它的中序 遍历。
同样中序遍历也有两种方法来实现,分别为递归法和迭代法,其中递归法的实现和前序遍历几乎一样,只是改变了存储时的顺序,中序遍历顺序为:先存储左子节点的值之后遍历根节点并对根节点进行同样的操作,左后遍历右节点并进行同样的操作。采用深度优先搜索自上而下进行遍历,需要一个辅助函数进行遍历。
递归代码如下:
vector<int> inorderTraversal(TreeNode* root) {
vector<int>ans;
if(root)
helper(root,ans);
return ans;
}
void helper(TreeNode *root,vector<int>&ans){
if(root->left!=nullptr)
helper(root->left,ans);
ans.push_back(root->val);
if(root->right!=nullptr)
helper(root->right,ans);
}
可看出中序遍历和前序遍历代码完全一样,只是存储时的顺序不一样。
接下来介绍递归法实现中序遍历,同样需要一个辅助栈来存储遍历的过程,这里同样提供一张图来讲解
迭代代码实现如下:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*>helper;
vector<int>res;
TreeNode *curr=root;
while(curr!=nullptr || !helper.empty()){
while(curr!=nullptr){ //一直遍历左子节点直到叶子节点,并将其入栈
helper.push(curr);
curr=curr->left;
}
curr=helper.top(); //栈顶出栈,并将其值存储到结果中
helper.pop();
res.push_back(curr->val);
curr=curr->right; //对右子节点进行同样的操作
}
return res;
}
后序遍历
给定一个二叉树,返回它的 后序 遍历。
二叉树的后续遍历采用递归实现时和前序中序代码类似,只是改变了存储顺序,后序遍历的顺序为:先遍历左节点,后遍历右节点,最后存储根节点,代码实现也十分简单。
递归法代码
vector<int> postorderTraversal(TreeNode* root) {
vector<int>res;
helper(root,res);
return res;
}
void helper(TreeNode*root,vector<int>&res){
if(root!=nullptr){
helper(root->left,res); //改变遍历顺序,先左节点,后右节点最后存储根节点数据。
helper(root->right,res);
res.push_back(root->val);
}
}
后序遍历的迭代实现有些复杂这里有一个巧妙的方法,即同中序遍历代码类似,只是先遍历所有的右子节点,再根节点,最后左子节点,在最后输出时将存储的数据倒序输出。这里仍然以图的方式给出。
迭代代码如下
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*>helper;
vector<int>res;
if(root==nullptr)return res;
helper.push(root);
while(!helper.empty()){
TreeNode *first=helper.top();
helper.pop();
res.push_back(first->val);
if(first->left!=nullptr){
helper.push(first->left);
}
if(first->right!=nullptr){
helper.push(first->right);
}
}
reverse(res.begin(),res.end());
return res;
}
层序遍历
给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。
例如:
给定二叉树: [3,9,20,null,null,15,7],
层序遍历同样有两种实现方式,不过其递归方式和前几种不一样,层序是广度优先搜索实现的,即需要先将某一层的遍历完再进入下一层,因此需要一个记录层数的变量level,设res代表结果列表,列表的长度代表树的层数,当列表长度和层数level相等时需要添加一个空列表,并将当前节点添加到列表res[level]中,对于非空左右子节点同样如此操作,即helper(node->left或node->right,level+1)
代码如下:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>>res;
helper(root,0,res);
return res;
}
void helper(TreeNode *root,int level,vector<vector<int>>&res){
if(root!=nullptr){
if(res.size()==level) //当列表长度和层数相等时,添加一个空列表
res.push_back(vector<int>{});
res[level].push_back(root->val); //将当前节点的值存入到结果数组中
helper(root->left,level+1,res); //对左右子节点进行同样的操作
helper(root->right,level+1,res);
}
}
接下来将使用迭代法实现层序遍历,不同的是这里需要使用辅助队列记录每层的节点,使用level来记录树的层数,遍历每一层时将每层的节点全部存入到队列中,再通过遍历队列将节点值存入到结果数组中。
中序遍历代码如下
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*>helper;
vector<vector<int>>res;
if(root==nullptr)return res;
helper.push(root);
int level=0;
while(!helper.empty()){
res.push_back(vector<int>{}); //每层添加一个空列表
int len_queue=helper.size(); //计算队列的长度
for(int i=0;i<len_queue;i++){ //遍历队列,同时将每个节点的非空子节点存入到队列中
TreeNode* first=helper.front();
helper.pop();
res[level].push_back(first->val);
if(first->left!=nullptr){
helper.push(first->left);
}
if(first->right!=nullptr){
helper.push(first->right);
}
}
level++;
}
return res;
}