LeetCode 二叉树的深度遍历和层次遍历(最详细讲解!)

二叉树的深度遍历(非递归)

题目描述:
二叉树的前序遍历:给定一个二叉树,返回它的前序遍历。
二叉树的中序遍历:给定一个二叉树,返回它的中序遍历。
二叉树的后续遍历:给定一个二叉树,返回它的后序遍历。

题目分析:
用传统的递归做法难度很小,在此不做讨论,本文讨论迭代方法。而网上的迭代方法基本一样,但对于初学者来讲晦涩难懂,本文把迭代的每一步操作与递归的操作一一对应起来,有助于新手理解。
比如我们拿中序遍历来举例,下面是递归方法的代码:

void inorder(TreeNode* root){
	if(root){
		inorder(root->left);
		handle(root->val);     //对当前结点进行某种处理
		inorder(root->right);
	}
}

下面我们来考虑迭代解决中序遍历问题,由于二叉树的遍历问题都涉及到回溯,所以我们考虑用栈的数据结构来处理,其实不难看出,在递归算法中每一次的递归调用inorder,都是把一个待处理的函数放入堆栈中.
整个过程大体为:

  1. 先传入一个结点(最开始为根节点),把inorder(root)函数放入堆栈中,然后在执行inorder(root)的第一步中,又调用了inorder(root->left),又把inorder(root->left)放入了堆栈中,以此类推,直到函数传入的根节点为NULL。
  2. 这时栈顶的函数为inorder(NULL),不做操作直接弹出,然后处理当前栈顶的函数,记为f1,由于f1在入栈时已经处理过inoder(root->left)这步,所以继续执行handel(root->val),然后继续执行inorder(root->right)。(执行inorder(root->right)相当于第一步传入一个结点)。
    我们通过以上的递归算法来构建迭代算法,每一步递归,在迭代算法中就相当于一个新节点进栈(类比上述的步骤1)或者处理当前结点,并把当前结点置为右子结点(类比上述的步骤2)。

具体中序遍历的迭代算法如下:

vector<int> inorderTraversal(TreeNode* root) {
        TreeNode* p;      //代表当前结点
        stack<TreeNode*> stk;  //用一个栈来存储结点
        vector<int> v;
        if(root != NULL){
            p = root;
            while((p!=NULL)||!stk.empty()){
                if(p!=NULL){    //当前结点不为空就进栈,并把当前结点置为左子结点,相当于递归算法中的步骤1
                    stk.push(p);
                    p = p->left;
                }
                else{          //结点为空时,就处理栈顶的非空结点,并把当前结点置为右子结点,相当于递归算法中的步骤2
                    TreeNode* temp = stk.top();
                    v.push_back(temp->val);
                    p = temp->right;
                    stk.pop();
                }
            }
        }
        return v;
    }

前序遍历和后序遍历的迭代思想与上述的思想一致,只不过前序遍历在结点进栈的时候就对当前结点进行处理,对于后序遍历会更难一些,因为需要判断上次访问的节点是位于左子树,还是右子树。若是位于左子树,则需跳过根节点,先进入右子树,再回头访问根节点;若是位于右子树,则直接访问根节点。(处理此问题可添加一个标记)。

前序遍历代码如下:

vector<int> preorderTraversal(TreeNode* root) {
        TreeNode* p;
        stack<TreeNode*> stk;
        vector<int> v;
        if(root != NULL){
            p = root;
            while((p != NULL)||!stk.empty()){
                if(p!=NULL){
                    v.push_back(p->val);
                    stk.push(p);
                    p = p->left;
                }
                else{
                    TreeNode* temp = stk.top();
                    p = temp->right;
                    stk.pop();
                }
            }
        }
        return v;
    }

后序遍历代码如下:

vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> stk;
        vector<int> v;
        TreeNode* p;
        int tag[100] = {0};   //用一个标记来辨别右子结点是否已被访问
        if(root != NULL){
            p = root;
            while((p != NULL)||!stk.empty()){
                if(p != NULL){
                    stk.push(p);
                    tag[stk.size()-1] = 0;
                    p = p->left;
                }
                else if(tag[stk.size()-1]==0){
                    tag[stk.size()-1] = 1;
                    p = stk.top()->right;
                }
                else{
                    v.push_back(stk.top()->val);
                    stk.pop();
                }
            }
        }
        return v;
    }

二叉树的层次遍历

题目描述
给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。

例如:
给定二叉树: [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回其层次遍历结果:

[
  [3],
  [9,20],
  [15,7]
]

题目分析:
二叉树的层次遍历可用队列的数据结构来实现,具体过程为:

  1. 树的根节点进队列。
  2. 队首元素出列,若不为空,则该出队元素的左右子结点入队。
  3. 循环2。

把上述的出队列的元素依次列出,即为层次遍历的结果。
对于此题,除了要求层次遍历,还要把每一层的元素单独存在一个vector容器中,只要对上述方法进行一下改进即可:记录每层需要的出列的元素个数。代码实现如下:

 vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> v;
        vector<int> help;     //辅助容器,用来存储每一层的元素
        queue<TreeNode*> q;
        if(root)
            q.push(root);
        while(!q.empty()){
            int num = q.size();   //该层元素的个数
            //出队num次
            for(int i = 1;i<=num;i++){
                TreeNode *temp = q.front();
                q.pop();
                if(temp){
                    help.push_back(temp->val);
                    q.push(temp->left);
                    q.push(temp->right);
                }
            }
            if(!help.empty()){
                v.push_back(help);
                help.clear();
            }
        }
        return v;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值