LeetCode 236. 二叉樹的最近公共祖先 極限效能演算法 比LeetCode 99%還快50倍
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)
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
相關文章
- LeetCode——最近公共祖先LeetCode
- 二叉樹的最近公共祖先二叉樹
- leetcode 235. 二叉搜尋樹的最近公共祖先LeetCode
- (117)235. 二叉搜尋樹的最近公共祖先(leetcode)LeetCode
- 二叉搜尋樹和二叉樹的最近公共祖先二叉樹
- 【leetcode 簡單】 第六十八題 二叉搜尋樹的最近公共祖先LeetCode
- 236、二叉樹的最近公共祖先 | 演算法(leetcode,附思維導圖 + 全部解法)300題二叉樹演算法LeetCode
- # 劍指 Offer 68 - II. 二叉樹的最近公共祖先二叉樹
- 最近公共祖先
- Git 中的演算法-最近公共祖先Git演算法
- 樹上問題/簡單演算法 LCA【最近公共祖先】演算法
- Day21 | 530.二叉搜尋樹的最小絕對差、501.二叉搜尋樹中的眾數 、236. 二叉樹的最近公共祖先二叉樹
- 二叉樹:距離最近的共同祖先二叉樹
- 程式碼隨想錄演算法訓練營day18 |530.二叉搜尋樹的最小絕對差 501.二叉搜尋樹中的眾數 236. 二叉樹的最近公共祖先演算法二叉樹
- 程式碼隨想錄演算法訓練營第18天| 530.二叉搜尋樹的最小絕對差, 501.二叉搜尋樹中的眾數 , 236. 二叉樹的最近公共祖先演算法二叉樹
- 二叉樹中兩個節點的最低公共祖先二叉樹
- lc235.二叉搜尋樹的最近公共祖先【①分別得到祖先序列,然後比較;②***同時查詢,找出分岔結點】
- 演算法學習筆記(5): 最近公共祖先(LCA)演算法筆記
- 樹上公共祖先(LCA)
- 程式碼隨想錄演算法訓練營day22 | leetcode 235. 二叉搜尋樹的最近公共祖先、701. 二叉搜尋樹中的插入操作、450. 刪除二叉搜尋樹中的節點演算法LeetCode
- Google S2 中的四叉樹求 LCA 最近公共祖先Go
- LeetCode-199-二叉樹的右檢視LeetCode二叉樹
- leetcode 199. 二叉樹的右檢視LeetCode二叉樹
- LeetCode199.二叉樹的右檢視LeetCode二叉樹
- 【LeetCode擊敗99%+】二叉樹路徑總和LeetCode二叉樹
- LCA最近公共祖先 線上演算法和離線演算法 模板演算法
- POJ 1330 LCA最近公共祖先 離線tarjan演算法演算法
- 「學習筆記」tarjan 求最近公共祖先筆記
- LeetCode-099-恢復二叉搜尋樹LeetCode
- 程式碼隨想錄day18 || 530 二叉搜尋樹最小差,501 二叉搜尋樹眾數,236 二叉搜尋樹最近公共祖先
- LeetCode 對稱二叉樹LeetCode二叉樹
- [Golang]力扣Leetcode—初級演算法—樹—二叉樹的最大深度Golang力扣LeetCode演算法二叉樹
- 【LeetCode-二叉樹】二叉樹前序遍歷LeetCode二叉樹
- 程式碼隨想錄演算法訓練營第十七天|leetcode654. 最大二叉樹、leetcode617.合併二叉樹、leetcode700.二叉搜尋樹中的搜尋、leetcode98.驗證二叉搜尋樹演算法LeetCode二叉樹
- 程式碼隨想錄演算法訓練營第22天 |二叉樹part07:235. 二叉搜尋樹的最近公共祖先、701.二叉搜尋樹中的插入操作、450.刪除二叉搜尋樹中的節點演算法二叉樹
- 程式碼隨想錄演算法訓練營第19天|235. 二叉搜尋樹的最近公共祖先 ,701.二叉搜尋樹中的插入操作,450.刪除二叉搜尋樹中的節點演算法
- 程式設計熊講解LeetCode演算法《二叉樹》程式設計LeetCode演算法二叉樹
- 程式碼隨想錄演算法訓練營第十四天|leetcode226. 翻轉二叉樹、leetcode101.對稱二叉樹、leetcode104.二叉樹的最大深度、leetcode111.二叉樹的最小深度演算法LeetCode二叉樹