二叉树相关算法整理(跟新中)

1.二叉树的遍历问题

1.1 非递归前中后序遍历

1)中序

void midprintf(node* Node){      
        if(Node==NULL)
            return ;
        node *p = Node;
        stack<node*> list;
        while(!list.empty() || p!=NULL){
            while(p) //一路往下压栈,直到最左的叶子节点
            {
                list.push(p);
                p = p->left;
            }
            p = list.top();
            list.pop();
            cout<<p->val<<" ";
            p = p->right; //转向右子节点 
        }
           cout<<endl;
    }

2)前序
前序和中序非常类似


    void preprintf(node* Node){
        if(Node==NULL)
            return ;
        stack<node*> list;
        node *p = Node;
        while(!list.empty() || p!=NULL){
            while(p)
            {
                cout<<p->val<<" ";
                list.push(p);
                p = p->left;
            }
            p = list.top();
            list.pop();
            p = p->right;
        }
            cout<<endl;
    }

3)后序
后序是相对复杂的,当父节点没有右节点,直接弹出栈打印并标记,若有右节点则需要重新压栈。

    void backprintf(node *Node){
            if(Node == NULL)
                return ;
            node *cur = Node;
            node *flag = NULL;
            stack<node*> list;
            while(cur)//一路往左,入栈
            {
                list.push(cur);
                cur = cur->left;
            }
            while(!list.empty())
            {
                cur = list.top();
                list.pop();
                if(cur->right==NULL || cur->right == flag)
                {
                    cout<<cur->val<<" ";
                    flag = cur;
                }else{ //有右树,把当前节点再次入栈,然后往右
                    list.push(cur);
                    cur = cur->right; 
                    while(cur)
                    {
                        list.push(cur);
                        cur = cur->left;
                    }
                }
            }
            return ;
    }

1.2 Morris遍历

不仅在遍历问题上,利用Morris遍历可以在很多其他问题上进行空间优化。
详见 关于Morris遍历

2.二叉树深度问题

2.1 求二叉树最大深度

1)递归

int getdepth2(node *p){
         if(p==NULL)
             return 0;

         int left = 1 + getdepth2(p->left);
         int right = 1 + getdepth2(p->right);

         return max(left,right);
     }

2)非递归

int getdepth(node *p){
            queue<node*> list;
            list.push(p);
            int height = 0;
            while(!list.empty()){
                height++;
                int len = list.size();
                while(len--)
                {
                    node *tmp =list.front();
                    list.pop();
                    if(tmp->left!=NULL)
                        list.push(tmp->left);
                    if(tmp->right!=NULL)
                        list.push(tmp->right);
                }
            }
            return height;
        }

2.2 求二叉树最小深度

1)递归
参照求最大深度,用递归的方式可以很快写出来。

    int getmindepth(TreeNode* root) {
        // write code here
        if(root == NULL)
            return 0;
        
        if(root->right == NULL)
            return getmindepth(root->left)+1;
        
        if(root->left == NULL)
            return getmindepth(root->right)+1;
            
        int left = 1 + getmindepth(root->left);
        int right = 1 + getmindepth(root->right);
         
        return min(left,right);
    }

2)非递归
非递归也类似。

    int getmindepth(TreeNode* root) {
        // write code here
        if(!root)
            return 0;
        if(!root->left&&!root->right)
            return 1;
        queue<TreeNode*> que;
        int depth = 0,len;
        que.push(root);
        while(!que.empty()){
            depth++;
            len = que.size();
            while(len--){
                TreeNode *node = que.front();
                que.pop();
                if(!node->left&&!node->right)
                    return depth;
                if(node->left)
                    que.push(node->left);
                if(node->right)
                    que.push(node->right);
            }
        }
        return depth;
       // return 0;
    }

3. 二叉树的构造与转换问题

3.1 给出前序中序遍历序列构造二叉树

由前序中序遍历序列,我们可以找出一下规律。前序序列第一个节点为根节点,在中序序列中找到该根节点,则其左边的序列为左子树,右边的序列为右子树。以这种方式不断将序列一分为二。

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        // write code here
        if(!inorder.size())
            return NULL;
        vector<int> left_pre,left_mid,right_pre,right_mid;
        int head = preorder[0];
        TreeNode* node = new TreeNode(head);
        int i;
        for(i = 0;i<inorder.size();i++)
        {
            if(inorder[i]!=head)
            {
                left_mid.push_back(inorder[i]);
                left_pre.push_back(preorder[i+1]);
            }else
                break;
        }
        
        for(i = i+1;i<inorder.size();i++)
        {
            right_mid.push_back(inorder[i]);
            right_pre.push_back(preorder[i]);
        }
        node->left = buildTree(left_pre,left_mid);
        node->right = buildTree(right_pre,right_mid);
        return node;
    }

3.2 给出后序中序遍历序列构造二叉树

同上题几乎一样,同样可以找出规律,后序序列的最后一个节点为根节点。

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        // write code here
        int len = postorder.size();
        if(!len)
            return NULL;
        vector<int> left_post,left_mid,right_post,right_mid;
        int head = postorder.back();
        TreeNode* node = new TreeNode(head);
        int i;
        for(i = 0;i<len;i++)
        {
            if(inorder[i]!=head)
            {
                left_mid.push_back(inorder[i]);
                left_post.push_back(postorder[i]);
            }else
                break;
        }
        
        for(i = i+1;i<len;i++)
        {
            right_mid.push_back(inorder[i]);
            right_post.push_back(postorder[i-1]);
        }
        node->left = buildTree(left_mid,left_post);
        node->right = buildTree(right_mid,right_post);
        return node;
    }

3.3 给出递增序列构造平衡搜索二叉树

给出一个1~n的序列,构造平衡搜索二叉树。
搜索二叉树的中序序列是递增序列,构造时需要划分左右子树,如果要求构造平衡搜索二叉树,只需要用递归的方式,每次将序列二分。

vector<TreeNode*> generateTrees(int n) {
        // write code here
        return func(1,n);
    }
    
    TreeNode* func(int left,int right){
       if(left>right)
       return NULL;
       int mid = (left+right)/2;
       TreeNode* node = new(mid);
       node->left = func(left,mid-1);
       node->right = func(mid+1,right);
       return node;
    }

3.4 给出递增序列构造所有可能的搜索二叉树

同上题,不过本题是构造出所有可能的二叉树,而上题平衡二叉树只是其中特殊的一种,本题更具有普遍性。
参照上题,不同的只是mid是如何确定的,平衡二叉树的mid以中间点,而本题的"mid”应该是通过循环,分别确定划分的位置。所以需要用两个vector来分别存储左右子树的每一种可能,再通过两层循环来构造。

vector<TreeNode*> generateTrees(int n) {
        // write code here
        return func(1,n);
    }
    
    vector<TreeNode*> func(int left,int right){
        vector<TreeNode*> res;
        if(left>right){
            res.push_back(NULL);
            return res;
        }
        for(int i = left;i<=right;i++){   //确定父节点
        vector<TreeNode*> lnode = func(left,i-1);   //存储左树可能出现的所有情况
        vector<TreeNode*> rnode = func(i+1,right);  //存储右树可能出现的所有情况
        for(int j = 0;j<lnode.size();j++)
            for(int k = 0;k<rnode.size();k++){  //类似于相乘,构造每一种情况
                TreeNode* head = new TreeNode(i);
                head->left = lnode[j];
                head->right = rnode[k];
                res.push_back(head);
            }
        }
        return res;
    }

3.5 同3.4,本题只需要求能构造出多少种搜索二叉树

使用动态规划的方式。
假设dp[i]表示i长度的序列能构造出不同二叉树的数目,初始可得dp[0] = dp[1] = 1。
首先要选择一个作为头结点,那么对于序列 1,2,3,4,5,如果选择3作为头结点,那么其二叉树数目应该是左边序列的二叉树数目乘以右边序列的二叉树数目,用式子表示即dp[2]*dp[2],因为1,2和4,5构造出二叉树的数目是一样的,其只和长度有关。
同理,选择1为根节点其值为dp[0]*dp[4];选择2位根节点其值为dp[1]*dp[3],以此类推。
所以dp[n]的值应该是从分别选择1~n作为根节点所得的值的累加和。

 int numTrees(int n) {
        // write code here
        int dp[n+1];
        for(int i = 2;i<=n;i++)
            dp[i] = 0;
        dp[0] = 1;
        dp[1] = 1;
        for(int i = 2;i<=n;i++)
            for(int j = 1;j<=i;j++){
                dp[i] += dp[j-1]*dp[i-j];
            }
        return dp[n];
    }

3.6 二叉树的序列化与反序列化

4. 树形dp问题

5. 花样打印二叉树

5.1 从上往下打印出二叉树的每个节点,同层节点从左至右打印。

利用队列,逐层打印。

    vector<int> PrintFromTopToBottom(TreeNode* root) {
      if(root==NULL)
      return vector<int>();    
        queue<TreeNode*> tree;
        vector<int> result;
         TreeNode* tmp = NULL;
          tree.push(root);
             while(!tree.empty())
             {
                 tmp = tree.front();
                 result.push_back(tmp->val);
                 if(tmp->left!=NULL)
                     tree.push(tmp->left);                 
                 if(tmp->right!=NULL)
                     tree.push(tmp->right);
                 tree.pop();
             }
            return result;
    }

5.2 从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

同上题,本题需要每一层输出一行,所以要通过深度来区分是否在同一层。同样是用队列。

 vector<vector<int> > Print(TreeNode* pRoot) {
    vector<vector<int>> result;
    vector<int> temp;
    queue<TreeNode*> que;
	queue<int> dque;
	int depth = 0;
    TreeNode* node;
    que.push(pRoot);
    dque.push(depth);
    while(!que.empty())
    {
    node = que.front();
    if(depth != dque.front())
    {
     result.push_back(temp);
     temp.clear();
     depth = dque.front();
     }
     que.pop();
     dque.pop();
     if(node)
     {
      que.push(node->left);
      que.push(node->right);
      dque.push(depth+1);
      dque.push(depth+1);
      temp.push_back(node->val);
     }     
 }
      return result;
}

5.3 之字形层序打印,(第一层从左向右,下一层从右向左,一直这样交替)

    vector<vector<int> > zigzagLevelOrder(TreeNode* root) {
        // write code here
        stack<TreeNode*> s1;
        stack<TreeNode*> s2;
         vector<vector<int>> res;
        if(root==NULL)
        return res;
        TreeNode *tmp;
        s1.push(root);
        while(!s1.empty()||!s2.empty())
        {
            vector<int> tmpres;
            while(!s1.empty())
            {
                tmp = s1.top();
                s1.pop();
                tmpres.push_back(tmp->val);
                if(tmp->left)
                    s2.push(tmp->left);
                if(tmp->right)
                    s2.push(tmp->right);
            }
            if(!tmpres.empty())
            {
                res.push_back(tmpres);
                tmpres.clear();
            }
            
            while(!s2.empty())
            {
                tmp = s2.top();
                s2.pop();
                tmpres.push_back(tmp->val);
                if(tmp->right)
                    s1.push(tmp->right);
                if(tmp->left)
                    s1.push(tmp->left);
            }
            
              if(!tmpres.empty())
                res.push_back(tmpres);
        }
        return res;
    }
  • 求二叉树的叶子节点数
    输入
    1)头结点
//递归求叶子节点数
int findleafnode(node* head)
{
    if(head==NULL)
        return 0;

    if(head->left==NULL&&head->right==NULL)
        return 1;

    return findleafnode(head->left)+findleafnode(head->right);
}
//非递归
int findleafnode2(node *head)
{
    node *tmp = NULL;
    stack<node*> list;
    if(head==NULL)
        return 0;
    list.push(head);
    int count = 0;
    while(!list.empty())
    {
         tmp = list.top();
         list.pop();
         if(tmp->left==NULL&&tmp->right==NULL)
                 count++;
         if(tmp->left!=NULL)
             list.push(tmp->left);
         if(tmp->right!=NULL)
             list.push(tmp->right);
    }
    return count;
}

2)中序遍历和前序遍历的序列

//pre传入前序序列,mid传入中序序列
int findleafnode(vector<int>pre,vector<int>mid)
{
    if(mid.empty())
        return 0;

    if(mid.size()==1)//即为叶子节点
    {
        return 1;
    }
    int gen;
    vector<int> pre_left,pre_right,mid_left,mid_right;
    for(size_t i = 0;i<mid.size();i++)//前序序列的第一个元素即为根节点,找到其在中序序列中的位置
    {
        if(pre[0]==mid[i])
        {
            gen = i;
            break;
        }
    }

    for(int i = 0;i<gen;i++//将中序序列从根节点处进行分割,前序序列也一样,是对应的
    {
        mid_left.push_back(mid[i]);
        pre_left.push_back(pre[i+1]);
    }
    for(size_t i = gen+1;i<mid.size();i++)
    {
        mid_right.push_back(mid[i]);
        pre_right.push_back(pre[i]);
    }
	//将分割好的子序列再进行递归
    return findleafnode(pre_left,mid_left)+findleafnode(pre_right,mid_right);
}

  • 遍历

二叉树有前中后序遍历,递归的实现较简单,下面主要是非递归的实现。

6. 完全二叉树

6.1 完全二叉树的叶子节点数

6.2 完全二叉树的节点数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值