LeetCode 236. 二叉樹的最近公共祖先 極限效能演算法 比LeetCode 99%還快50倍

superzmy悽臨雨發表於2020-12-15

LeetCode 236. 二叉樹的最近公共祖先
給定一個二叉樹, 找到該樹中兩個指定節點的最近公共祖先。

百度百科中最近公共祖先的定義為:“對於有根樹 T 的兩個結點 p、q,最近公共祖先表示為一個結點 x,滿足 x 是 p、q 的祖先且 x 的深度儘可能大(一個節點也可以是它自己的祖先)。”

LeetCode題解:C++非遞迴演算法,4ms打敗100%,比99%實現的程式碼還快50倍

執行用時:4 ms, 在所有 C++ 提交中擊敗了100.00%的使用者
記憶體消耗:14 MB, 在所有 C++ 提交中擊敗了85.08%的使用者

lowestCommonAncestor_my重複一萬次演算法,執行耗時一共60ms。
lowestCommonAncestor_other重複一千次演算法,執行耗時一共324ms。
顯然my演算法比別人快50倍

/* 執行結果:
通過
顯示詳情
執行用時:8 ms, 在所有 C++ 提交中擊敗了100.00%的使用者
記憶體消耗:13.9 MB, 在所有 C++ 提交中擊敗了98.99%的使用者

執行用時:4 ms, 在所有 C++ 提交中擊敗了100.00%的使用者
記憶體消耗:14 MB, 在所有 C++ 提交中擊敗了85.08%的使用者


lowestCommonAncestor_my重複一萬次演算法,執行耗時一共60ms。
lowestCommonAncestor_other重複一千次演算法,執行耗時一共324ms。
顯然my演算法比別人快50倍
*/

#pragma GCC optimize(3)
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("unroll-loops")

static auto x = []() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    return 0;
}();


/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
static TreeNode* stackForLCA[100];
static TreeNode* stackForExist[100];
static TreeNode* stackForExistB[100];

class Solution {
public:

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        /*volatile int count = 0;
        for (volatile int i = 0; i < 999; ++i)
            count += lowestCommonAncestor_my(root, p, q) != NULL;
        if (count != 999) throw(1);*/
        return lowestCommonAncestor_my(root, p, q);
    }
    TreeNode* lowestCommonAncestor_my(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == p || root == q)
            return root;
        if (p == q)
            return p;
        if (auto x = exist2(p, q); x)
            return x;
        //if (exist(p, q)) // p下找q找到了
        //    return p;
        //else if (exist(q, p))
        //    return q;

        // 兩點不互相包含

        // 一個棧,int&1 表示這個節點已展開,否則未展開。
        // 對於未展開的節點,變為展開,入棧其兩個子節點,不斷展開尋找p和q之一
        // 已展開的再被讀取到,則彈出棧
        // 一旦找到p或q之一
        // 切換到另一種演算法來使用這個棧:
        //   在未展開節點x裡直接dps搜尋other
        //   一旦x存在other
        //   則從x所在棧位置向前找一個已展開節點,即公共節點
        TreeNode** pStackTop;
        pStackTop = stackForLCA;

        *++pStackTop = root;
        
        while(pStackTop > stackForLCA)
        {
            TreeNode* cur = *pStackTop;
            if ((intptr_t)cur & 1) //表示節點展開過
            {
                pStackTop--;
            }    
            else //表示節點未展開過
            {
                (intptr_t&)*pStackTop |= 1;
go_quick:
                __builtin_prefetch(cur->left);
                if (cur == p || cur == q)
                {
                    TreeNode* other;
                    other = (TreeNode*)((intptr_t)cur ^ (intptr_t)p ^ (intptr_t)q);
                    // 當前節點是p/q之一,接下來只要在目前棧上未展開的節點上找到other
                    // 然後那個時候棧的最右側已展開節點,即公共父節點
                    while (--pStackTop > stackForLCA)
                    {
                        if (((intptr_t)*pStackTop & 1) == 0)
                            if (*pStackTop && (*pStackTop == other || exist(*pStackTop, other)))
                            {
                                while (--pStackTop > stackForLCA)
                                {
                                    if ((intptr_t)*pStackTop & 1)
                                        return (TreeNode*)(1 ^ (intptr_t)*pStackTop);
                                }
                            }
                    }
                }
                if (cur->right)
                    *++pStackTop = cur->right;
                if (cur->left)
                {
                    cur = cur->left;
                    (intptr_t&) *++pStackTop = 1 | (intptr_t)cur;
                    goto go_quick;
                }    
            }
        }
        return NULL;
    }
    
    [[noinline]] TreeNode* exist2(TreeNode* p, TreeNode* q)
    {
        if (p == q)
            return p;
        TreeNode** pStackTopP; pStackTopP = stackForExist;
        TreeNode** pStackTopQ; pStackTopQ = stackForExistB;
        *++pStackTopP = q;
        *++pStackTopQ = p;
        
        while (true)
        {
            //if (! ((pStackTop - stackForExist) < sizeof(stackForExist) / sizeof(*stackForExist)) )
            //    throw(0);
            if (pStackTopP > stackForExist)
            {
                TreeNode* cur = *pStackTopP--;
                if (cur->left == p || cur->right == p)
                    return q;
                if (cur->right)
                    *++pStackTopP = cur->right;
                if (cur->left)
                    *++pStackTopP = cur->left;
            }
            else
                goto go_searchQ_only;

            
            if (pStackTopQ > stackForExistB)
            {
                TreeNode* cur = *pStackTopQ--;
                if (cur->left == q || cur->right == q)
                    return p;
                if (cur->right)
                    *++pStackTopQ = cur->right;
                if (cur->left)
                    *++pStackTopQ = cur->left;
            }
            else
                goto go_searchP_only;
        }
        return NULL;
    go_searchQ_only:
        while (pStackTopQ > stackForExistB)
        {
            TreeNode* cur = *pStackTopQ--;
            if (cur->left == q || cur->right == q)
                return p;
            if (cur->right)
                *++pStackTopQ = cur->right;
            if (cur->left)
                *++pStackTopQ = cur->left;
        }
        return NULL;
    go_searchP_only:
        while (pStackTopP > stackForExist)
        {
            TreeNode* cur = *pStackTopP--;
            if (cur->left == p || cur->right == p)
                return q;
            if (cur->right)
                *++pStackTopP = cur->right;
            if (cur->left)
                *++pStackTopP = cur->left;
        }
        return NULL;
    }
    bool exist(TreeNode* node, TreeNode* p)
    {
        if (node->left == p)
            return true;
        else if (node->right == p)
            return true;
        
        TreeNode** pStackTop;
        pStackTop = stackForExist;
        if (node->right)
            *++pStackTop = node->right;
        if (node->left)
            *++pStackTop = node->left;
        
        while (pStackTop > stackForExist)
        {
            //if (! ((pStackTop - stackForExist) < sizeof(stackForExist) / sizeof(*stackForExist)) )
            //    throw(0);

            TreeNode* cur = *pStackTop--;
            if (cur->left == p || cur->right == p)
                return true;
            if (cur->right)
                *++pStackTop = cur->right;
            if (cur->left)
                *++pStackTop = cur->left;
        }

        return false;
    }



    TreeNode* lowestCommonAncestor_other(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==NULL) return NULL;
        if(root==p||root==q) return root;           //在同一支,父子孫節點

        TreeNode*left=lowestCommonAncestor_other(root->left, p, q);
        TreeNode*right=lowestCommonAncestor_other(root->right, p, q);

        if(left==NULL)  return right;
        else if(right==NULL) return left;
        else return root;    //root在p,q中間  (left!=NULL&&right!=NULL) 
    }
};

作者:qi-lin-yu
連結:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/solution/cfei-di-gui-suan-fa-12msda-bai-99-by-qi-535rq/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

相關文章