leetcode 刷题:验证二叉树的前序序列化

题目描述

    序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #。给一个二叉树前序序列化的例子如:
    字符串 “9,3,4,#,#,1,#,#,2,#,6,#,#”,其中 # 代表一个空节点。
    给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法。
    每个以逗号分隔的字符或为一个整数或为一个表示 null 指针的 ‘#’ 。
    你可以认为输入格式总是有效的,例如它永远不会包含两个连续的逗号,比如 “1,3” 。

示例 1:

输入: “9,3,4,#,#,1,#,#,2,#,6,#,#”
输出: true
示例 2:

输入: “1,#”
输出: false
                  来源:力扣(LeetCode)
 

(一)构建槽位的思路

构建槽位

    对于这道题目,由于是前序遍历,所以序列中父(根)结点总是在子节点的前面。对于这个特性,我们可以考虑构建一个槽位,这个槽位可以理解为一个坑位,将序列遍历,从头节点开始,槽位个数为一,如果头节点不为空(如果为空则不构成二叉树)则槽位个数加2。(因为每个非空节点会产生两个字节点,所以创建两个槽位),同时因为头节点要占一个槽位,所以槽位个数减1。之后继续遍历子节点,遇到非空子节点则槽位个数加2再减1,遇到空子节点则槽位个数减1,继续遍历。如果在遍历的过程中,槽位个数出现负数,说明该序列不符合二叉树前序遍历序列,因为槽位被用完了。整个序列遍历完后,正确的二叉树前序序列的槽位个数应该为0。基于这种槽位的思想,我们就能判定一个序列是否为二叉树前序遍历序列。
 

C++代码实现:

class Solution {
public:
    bool isValidSerialization(string preorder) {
        vector <string> stack; //定义存储将序列中的”,“去除之后的序列
        string temp="";
        for(int i=0;i<preorder.length();i++){
             if(preorder[i]!=','){
              temp.push_back(preorder[i]);
                                 }
              if(preorder[i]==','){
                   stack.push_back(temp);
                    temp="";
                                  }
                //preorder最后一个字符处理                  
              if(i==preorder.length()-1){
                   string s1;
                   char s[2]={preorder[i],0};
                   s1=s;
                   stack.push_back(s1);
                                        }
                                            }
        //判断二叉树节点数量                         
        if((stack.size()+1)%2!=0){ 
          return false;
                                 } 
        int slot=1;
        //构建槽位
        for (auto ch:stack){
            slot--;
            if(slot<0){
              return false;
                      }
            if(ch!="#"){
              slot=slot+2;
                       }
                           }
        if(slot!=0){
          return false;
                   }
        
         return true;
                                               } 
};

 

(二)在所有子树中空节点的数量比非空节点数量多1的思想

对二叉树前序遍历的观察

    还是利用之前的一个二叉树前序遍历序列[9,3,4,#,#,1,#,#,2,#,6,#,#]来举例进行观察。在这个序列中,我们能够发现,对于合理的二叉树,空节点的数量总是比非空节点数量多1。
    再由于前序遍历的顺序是根左右。我们可以考虑维护一个栈,遇到"#“判断栈是否为空,若为空则判断当前的”#"是否为序列最后一个字符。如果是,说明该序列是二叉树前序遍历序列。如果不是,则说明该序列不是二叉树前序遍历序列。同时,我们应该还考虑到一种情况是当序列遍历完成后,栈中还有数字的情况。这种情况其实也是不合理的二叉树。
 

C++代码实现

class Solution {
public:
    bool isValidSerialization(string preorder) {
         if(preorder.empty()){
             return false;
         }
         stack <bool> s;
         for(int i=0;i<preorder.length();i++){
            if(preorder[i]=='#'){
             //判断该"#"是否为序列最后一个字符
              if(s.empty()){
               return i==preorder.length()-1;
                           }
              else{
              //如果栈不为空,则出栈
                s.pop();
                i++; //这里是为了跳过","
                   }
                                }
             else{
                while((i < preorder.size() && preorder[i] != ',')){ 
                       i++;     //这里是为了将字符组成一个整体的字符串                                         
                                                                  }
                    s.push(0);
                  }
                                              }
         return false ;

    }
};

 

总结

    当用两种方法实现后,作者发现第二种方法虽然巧妙,但是不容易想到。对作者本身而言,第一种方法更好想到也更好理解和记忆一些。 但相应的,时间和空间复杂度也相对较高。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值