1. 程式人生 > >leetcode 951. 翻轉等價二叉樹- 遞迴演算法,改寫迭代失敗

leetcode 951. 翻轉等價二叉樹- 遞迴演算法,改寫迭代失敗

題目描述:

我們可以為二叉樹 T 定義一個翻轉操作,如下所示:選擇任意節點,然後交換它的左子樹和右子樹。

只要經過一定次數的翻轉操作後,能使 X 等於 Y,我們就稱二叉樹 X 翻轉等價於二叉樹 Y。

編寫一個判斷兩個二叉樹是否是翻轉等價的函式。這些樹由根節點 root1root2 給出。

 

示例:

輸入:root1 = [1,2,3,4,5,6,null,null,null,7,8], root2 = [1,3,2,null,6,4,5,null,null,null,null,8,7]
輸出:true
解釋:We flipped at nodes with values 1, 3, and 5.

 

提示:

  1. 每棵樹最多有 100 個節點。
  2. 每棵樹中的每個值都是唯一的、在 [0, 99] 範圍內的整數。

思路:

題目是指,有兩顆二叉樹,對其中一顆樹進行操作,交換它的某些節點的左右子樹(注意是子樹不是孩子節點的val),可以使它變為另一棵樹。這樣的兩棵樹,稱為等價二叉樹。

觀察一下可以發現,交換子樹對父節點沒有任何影響。如果兩個樹的某處父節點不同,那對它們的子節點進行操作於事無補。所以,我們應該從根節點往葉子節點來考察。

從上往下考察樹,讓人很容易想到遞迴。等價二叉樹中存在遞迴的現象嗎?如果root1和root2是等價的,那麼它要滿足兩個條件:

1,root1->val == root2->val

2,假定我們對root2進行操作,使它與root1相同。那麼需要考慮問題:要交換root2的左右子樹嗎?要麼不交換,使得root1和root2相同;要麼交換,使得它們相同。所以,滿足下面兩個條件之一即可:

   a) root1的左子樹和root2的左子樹等價,root1的右子樹和root2的右子樹等價

  b) root1的左子樹和root2的右子樹等價,root1的右子樹和root2的左子樹等價

這裡很明視訊記憶體在遞迴了。設定遞迴出口:兩顆空樹一定是等價的。由於從上往下比較,演算法一定會到達葉子節點,繼而抵達遞迴出口,然後返回結果。程式碼實現如下:

bool flipEquiv(TreeNode* root1, TreeNode* root2) {
        if((root1 || root2) == NULL) return true;//遞迴出口
        //1,根節點的val相同。否則一定不等價
        if((root1 && root2) == NULL) return false;//一定不等價
        if(root1->val != root2->val) return false;
        //2,兩顆左子樹等價並且兩顆右子樹等價,
        //或者,root1的左子樹等價root2的右子樹,root1的右子樹等價root2的左子樹
        return (flipEquiv(root1->left,root2->left)&&flipEquiv(root1->right,root2->right))    
       || (flipEquiv(root1->left,root2->right)&&(flipEquiv(root1->right,root2->left)));    
    }

在極端(返回值為 true) 的情況下,兩棵樹的所有節點一一比對,所以時間複雜度應該是O(n)。測試執行時間 4ms。

現在想想能不能對程式碼優化。

可以用條件語句把a) 和 b)明確分開嗎?畢竟它們大部分情況必然有一個是false(同一個root左孩子的val和右孩子的val不等),此時只需要考慮val相等的一對子樹。但是,把這兩種情況完全分開的條件太繁瑣了(存在這種情況:那一層次的四個孩子節點的val值可能全部相同),我不太想犧牲程式碼的簡潔、易讀去換取少量的效能提升。

可以增加遞迴出口嗎?其實到了葉子節點這裡,就可以直接判定是否等價了,無需再往下深入。程式碼稍作改動,增加遞迴出口:

    //當root1和root2同為葉子節點
    if(((root1->left || root1->right)||(root2->left||root2->right))==NULL)
        {
            if(root1->val == root2->val) return true;
            else return false;
        }

測試執行時間 4ms。

再來想一想,能不能把它改成迴圈。先假設我們有一個棧,可以用於儲存節點。裡面初值是root1和root2,每次出棧至少出一對。對於任意兩個結點的四個孩子,要判斷4對孩子節點是否等價...這四個值可真可假。對它們進行組合、判斷,如果條件a和條件b都不滿足,就可以返回false了。而問題在於:條件a與條件b是和後面所有節點相關的,如果四個孩子節點的val值都相同,根本沒有辦法在遍歷到某對節點時,就說它們等價或者不等價,那麼條件a和條件b都有可能成立,也都有可能不成立。這樣的話,這4對孩子節點,都要入棧。那麼出棧的時候,哪些節點對應條件a,哪些節點對應條件b?在這一層可以判斷清楚嗎?假如這一層所有孩子節點val又相同,又要放到下一層去判斷。在一次迴圈中要判斷的條件會動態變化,越來越複雜,而不是固定的。我當前的能力,還做不到這樣。

那先就這樣吧,以後再試。