验证二叉树的前序序列化

前言

最近自己的学长面试的时候遇到这一道题了,然后把这道题出给了我。我的第一想法是根据前序遍历建立一个树结构,然后根据能否建立树来判断。但是题目要求不能重建树,所以自己确实不会做。在网上看了别人的解答才恍然大悟!这里总结一下大神们的解法。

关于istringstream

首先我们肯定要用istringstream来操作字符串,C++里面没有像Java那样有字符串的split函数(可以直接分割任意字符串);在C++里我们只能用getline这个函数从用户处获得字符,将字符串流的内容都存到一个vector数组中。我们可以先看一个简单的例子:

int main()
{
    istringstream in("a,b,c,d,e,d,f");
    string t = "";
    while (getline(in, t, ','))
    {
        cout << t;
        Sleep(100);
    }
    system("pause");
    return 0;
}

istringstream类用于执行C++风格的串流的输入操作。
ostringstream类用于执行C风格的串流的输出操作。
strstream类同时可以支持C风格的串流的输入输出操作。
这个函数是从string对象中读取字符到in中,然后在执行while (getline(in, t, ','))的时候表示遇到','就将已经读到的字符输出。上面的代码输出的结果为

abcdedf

解法一

通过仔细观察我们可以发现两个规律:1.数字的个数总是比#号少一个;2.最后一个一定是#号。
所以我们可以根据这两个规律,将最后一个字符“#”单独进行考虑,那么此时数字的个数和#的个数应该相同。所以可以初始化一个计数器专门记录数字的个数,当遇到数字则计数器+1,遇到#号-1;这样遍历到除了最后一个字符,则一定会有计数器=0!。所以最后遍历完的判断就是计数器是否等于0与最后一个字符是不是#。
当然还有一些小细节要处理,比如“#,9,3,4,#”与“9,3,#,#,#,1”这两个错误案例,根节点首先不能为#,同时要确保在[0,i]这个范围内#的个数是不大于数字的个数的。所以可以通过在循环中判断是否会有计数器小于0的情况,代码如下:

class Solution {
public:
    bool isValidSerialization(string preorder) {
        istringstream in(preorder);
        string t="";
        int cnt=0;
        while(getline(in,t,','))
            v.push_back(t);
        
        for(int i=0;i<v.size()-1;i++)
        {
            if(v[i]=="#")
                
            {
              if(cnt==0)
                    return false;
                cnt--;
            }  
            else
                cnt++;
        }
        return cnt==0 && v.back()=="#";
    }
private:
    vector<string> v;
};

解法二

解法二是边解析边判断,遇到不合题意的情况直接返回false,而不用全部解析完再来验证是否合法,提高了运算的效率。

class Solution {
public:
    bool isValidSerialization(string preorder) {
        istringstream in(preorder);
        string t = "";
        int degree = 1;
        bool degree_is_zero = false;;
        while (getline(in, t, ',')) {
            if (degree_is_zero) return false;
            if (t == "#") {
                if (--degree == 0) degree_is_zero = true;
            } else ++degree;
        }
        return degree == 0;
    }
};

解法三

这种解法就更加巧妙了,连字符串解析都不需要了,用一个变量capacity来记录能容忍"#"的个数,跟上面解法中的degree一个作用,然后我们给preorder末尾加一个逗号,这样可以处理末尾的"#"。我们遍历preorder字符串,如果遇到了非逗号的字符,直接跳过,否则的话capacity自减1,如果此时capacity小于0了,直接返回true。此时再判断逗号前面的字符是否为"#",如果不是的话,capacity自增2。这种设计非常巧妙,如果逗号前面是"#",我们capacity自减1没问题,因为容忍了一个"#";如果前面是数字,那么先自减的1,可以看作是初始化的1被减了,然后再自增2,因为每多一个数字,可以多容忍两个"#",最后还是要判断capacity是否为0,跟上面的解法一样,我们要补齐"#"的个数,少了也是不对的,参见代码如下:

class Solution {
public:
    bool isValidSerialization(string preorder) {
        int capacity = 1;
        preorder += ",";
        for (int i = 0; i < preorder.size(); ++i) {
            if (preorder[i] != ',') continue;
            if (--capacity < 0) return false;
            if (preorder[i - 1] != '#') capacity += 2;
        }
        return capacity == 0;
    }
};

猜你喜欢

转载自www.cnblogs.com/yunlambert/p/9188764.html