数据结构之二叉树1(前序中序后序层序遍历,重建二叉树)

本文详细介绍了二叉树的前序、中序、后序遍历的递归和非递归解法,以及层序遍历。还讨论了如何根据中序和前序或后序序列重建二叉树的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 二叉树前序后序中序遍历 递归非递归解法

2  二叉树层序遍历;二叉树zigzag层序遍历

3 从中序和前序重建二叉树;从中序和后序重建二叉树



1 二叉树前序后序中序遍历

- 二叉树前序遍历,前序遍历方法是先访问根节点,然后再访问左右节点。

前序遍历的递归和非递归解法:

 //前序递归遍历
void preOrderRec(TreeNode *root)
{
     if(root == NULL) return;
     visit(root);
     preOrder(root->left);
     preOrder(root->right);
}

 //前序非递归遍历
void preOrder(TreeNode *root)
{
    stack<TreeNode *> s;

    while ((NULL != root) || !s.empty())
    {
        if (NULL != root)
        {
            visit(root);
            s.push(root);
            root = root->left;
        }
        else
        {
            root = s.top();
            s.pop();
            root = root->right;
        }
    }
}

- 二叉树中序遍历

中序遍历是先访问左子树,然后访问根节点,最后访问右子树

//递归中序遍历
void inorderTraversal(TreeNode *root)
{
     if(root == NULL) return;
     
    inorderTraversal(root->left);
     visit(root);
    inorderTraversal(root->right);
}

//非递归中序遍历   
 vector<int> inorderTraversal(TreeNode *root) {
        vector<int> result;
        vector<TreeNode*> Stack;
        if(root == NULL)
            return result;
        TreeNode * current = root;

        while(current != NULL || !Stack.empty())
        {
            if(current != NULL){
                //左子树压入栈内
                Stack.push_back(current);
                current = current->left;
            }
            else
            {          
                current = Stack.back();
                Stack.pop_back();
                result.push_back(current->val);
                current = current->right;
            }           
        }
        return result;   
    }

- 二叉树后序遍历

按照左-右-中的顺序访问,第一次遇到右节点的时候先入栈,并不访问,等到后序先把对应的左节点访问之后,才能轮到右节点,所以使用flag进行标记。

// 后序递归遍历
void postorderTranversal(TreeNode *root)
{
     if(root == NULL) return;
     
     preOrder(root->left);
     preOrder(root->right);
     visit(root);
}
//后序非递归遍历:主要是要有一个标记来表示右子树是否访问过:
void postorderTranversal(TreeNode * root)
{
     if(!root) return;

     stack<TreeNode *> Stack;
     TreeNode *lastvisit = NULL;

     while(root != NULL || !Stack.empty())
     {
          //压入左子树
          while(root!= NULL)
          {
               Stack.push_back(root);
               root = root -> left;
          }
          TreeNode * current = Stack.top();
          //current右子树为空说明到了最左,肯定要先访问
          //lastvisited等于current右子树说明右节点已经访问过一次了,
          //现在可以访问current节点了。保证的是右节点一定比根节点先被访问到。
          if(current -> right && lastvisited != (current->right))
          {
               current = current->right;
          }
          else
          {
               int data = current ->val;
               lastvisited = current;
               Stack.pop();
          }
     }

}



2 二叉树层序遍历;二叉树zigzag层序遍历

在二叉树层序遍历的时候,第一种思想是:利用queue队列的性质,要保持一个当前level的队列,和下一level的队列。首先依次访问当前level队列的每一个节点,然后把这些节点的儿子们依次放入下一level的队列中,直到当前level队列的每一个节点都被访问到了,这时候把下一level队列作为当前level的队列,同时继续向下循环。这个属于广度优先BFS。

为了节省空间,使用一个queue,但是用两个变量来记录当前level和下一level剩余的待访问节点数。

 vector<vector<int> > levelOrder(TreeNode *root) {
        vector<vector<int>> result;
        if (!root) return result;
        vector<int> tmp;
   
        queue<TreeNode*> nodesQueue;
        int nodesInCurrentLevel = 1;
        int nodesInNextLevel = 0;//分别记录当前level和下一level的节点数。
        nodesQueue.push(root);
       
        while (!nodesQueue.empty()) {
            TreeNode *currNode = nodesQueue.front();
            nodesQueue.pop();
            nodesInCurrentLevel--;
            if (currNode) {
                tmp.push_back(currNode->val);
                nodesQueue.push(currNode->left);
                nodesQueue.push(currNode->right);
                nodesInNextLevel += 2;
            }
            if (nodesInCurrentLevel == 0) {
                result.push_back(tmp);
                tmp.clear();
                nodesInCurrentLevel = nodesInNextLevel;
                nodesInNextLevel = 0;
            }
        }
        result.pop_back();
        return result;
    }

第二种思想是深度优先DFS,先达到最深,然后对每一个深度进行递归访问:

    vector<vector<int> > levelOrder(TreeNode *root) {
        vector<vector<int>> result;
       
        if(root == NULL) return result;    
        int depth = getMaxDepth(root,1);
       
        for(int i = 1; i<= depth ;i++){
            vector<int> tmp;
            tranverseInDepth(root, i,tmp);
            result.push_back(tmp);
        }
        return result;
    }
   
    int getMaxDepth(TreeNode * root,int depth)
    {
        if(root == NULL) return depth-1;
        int left = getMaxDepth(root->left,depth+1);
        int right = getMaxDepth(root->right,depth+1);
       
        return left>right?left:right;
    }
   
    void tranverseInDepth(TreeNode* root, int index ,vector<int> &tmp)
    {
        if(!root) return;
        if(1 == index)
        {
            tmp.push_back(root->val);
        }
        else{
            tranverseInDepth(root->left,index-1,tmp);
            tranverseInDepth(root->right,index-1,tmp);
        }
    }

对于zigzag访问的话则是这样子:首先访问根节点,然后从右向左访问根节点的下一层,然后自左向右访问再下一层,如此循环往复。这里使用stack来保存一层的节点,通过访问顺序的变化,实现zigzag的效果。

    vector<vector<int> > zigzagLevelOrder(TreeNode *root) {
        stack<TreeNode *> S ,Q;
        vector<vector<int>> result;
        if(root == NULL) return result;
       
        TreeNode * cur = root;
        Q.push(cur);
        bool zig = true;
        while(!Q.empty() || !S.empty())
        {
            vector<int> tmp;
            if(zig)
            {
                while(!Q.empty())
                {
                    TreeNode * t = Q.top();
                    Q.pop();
                    tmp.push_back(t->val);
                    if(t->left != NULL)
                        S.push(t->left);
                    if(t->right != NULL)
                        S.push(t->right);
                }
                result.push_back(tmp);
                tmp.clear();
                zig = false;
            }
            else
            {
                while(!S.empty())
                {
                    TreeNode * s = S.top();
                    S.pop();
                    tmp.push_back(s->val);
                    if(s->right != NULL)
                        Q.push(s->right);
                    if(s->left != NULL)
                        Q.push(s->left);
                }
                result.push_back(tmp);
                tmp.clear();
                zig = true;
            }
        }
        return result;
    }


3 从中序和前序重建二叉树;从中序和后序重建二叉树

- 从中序和前序重建二叉树
注意到前序每次都是先访问根节点,所以通过在中序序列中搜索根节点,可以有效的把中序序列分成左子树部分和右子树部分。如果要有效的查询前序中元素在中序中的索引,比较好的方法是使用map或者hashtable。offset这个变量是为了适应map中找到的索引值在新的inorder数组中的值而引入的。

 map<int,int>  mapIndex;
    TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) {

        buildMap(inorder);
        int n = preorder.size();
        return buildTreeHelper(preorder,inorder,n,0);
       
    }
    void buildMap(vector<int> inorder)
    {
        for(int i = 0; i < inorder.size() ; i++)
            mapIndex[inorder[i]] = i;
    }
    TreeNode* buildTreeHelper(vector<int>& pre, vector<int> &in, int n, int offset)
    {
       if(n == 0) return NULL;
       int val = pre[0];
       int i = mapIndex[val] - offset;
       TreeNode * root = new TreeNode(val);

       // pre跳过第一个元素
       vector<int> tmp1(pre.begin()+1,pre.end());
       root -> left = buildTreeHelper(tmp1, in , i,offset);

       if(i+1 > in.size() || i+1 > pre.size()) return NULL;
       //分别跳过左子树和根节点 以及 根节点和左子树
       vector<int> tmp2(in.begin()+i+1,in.end());
       vector<int> tmp3(pre.begin()+i+1,pre.end());
       root -> right = buildTreeHelper(tmp3,tmp2,n-i-1, offset +i + 1);
   
        return root;
    }

- 对于后序和中序进行重建,原理基本相同,只是根节点对于后序来说是在数组的末尾位置。
TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) {

        buildMap(inorder);
        int n = postorder.size();
        return buildTreeHelper(inorder,postorder,n,0);  
    }
    TreeNode* buildTreeHelper(vector<int> &in, vector<int> &post,int n, int offset)
    {
        if(n == 0) return NULL;
        int val = post[n-1];
        int i = mapIndex[val] - offset;
        TreeNode * root = new TreeNode(val);
        root->left = buildTreeHelper(in,post,i,offset);
        if(i+1 > in.size() || i > post.size()) return NULL;
        // inorder跳过左子树和根节点
        vector<int> tmp2(in.begin()+i+1,in.end());
        //postorder跳过左子树
        vector<int> tmp3(post.begin()+i,post.end());
        root->right = buildTreeHelper(tmp2,tmp3,n-i-1,offset+i+1);
       
        return root;
    }









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值