1. 程式人生 > >驗證二叉樹的前序序列化

驗證二叉樹的前序序列化

cto push_back line amp for out 執行 單獨 AS

前言

最近自己的學長面試的時候遇到這一道題了,然後把這道題出給了我。我的第一想法是根據前序遍歷建立一個樹結構,然後根據能否建立樹來判斷。但是題目要求不能重建樹,所以自己確實不會做。在網上看了別人的解答才恍然大悟!這裏總結一下大神們的解法。

關於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;
    }
};

驗證二叉樹的前序序列化