LeetCode 236. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
C++非递归算法,0ms 6.7MB 击败100% 无敌快。重复1000次只要4ms,击败10000%
显示详情 执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:6.7 MB, 在所有 C++ 提交中击败了100.00%的用户
显示详情
执行用时:8 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:13.9 MB, 在所有 C++ 提交中击败了98.99%的用户
执行用时:4 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:14 MB, 在所有 C++ 提交中击败了85.08%的用户
lowestCommonAncestor_my重复一【万】次算法,执行耗时一共48ms。
lowestCommonAncestor_other重复一千次算法,执行耗时一共296ms。
lowestCommonAncestor_other2重复一千次算法,执行耗时一共1880ms。
显然my算法比别人快50倍
/* 执行结果:
显示详情 执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:6.7 MB, 在所有 C++ 提交中击败了100.00%的用户
显示详情
执行用时:8 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:13.9 MB, 在所有 C++ 提交中击败了98.99%的用户
执行用时:4 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:14 MB, 在所有 C++ 提交中击败了85.08%的用户
lowestCommonAncestor_my重复一【万】次算法,执行耗时一共48ms。
lowestCommonAncestor_other重复一千次算法,执行耗时一共296ms。
lowestCommonAncestor_other2重复一千次算法,执行耗时一共1880ms。
显然my算法比别人快50倍
另外破坏root子节点,造成内存泄露,这样leetcode退出程序时销毁内存时跳过这些节点直接导致内存从14MB→10MB
事实上c++提交里内存低于13.7MB的全是有作弊嫌疑的。
因为本题测主要耗时在输入读取上(平均10ms) 读入和程序基本内存需要10MB
LCA算法耗时0ms 0MB内存
释放耗时未知(可能是1ms),需要4MB内存
*/
#define RuinTree 1 // 0或1,破坏树可节省内存
#define MyIO 1 // 0或1,自行IO
#define Repeat 1 // 1或1000 10000,用于测试算法重复多次以后和其他算法的差距
#define Implement lowestCommonAncestor_my
// lowestCommonAncestor_my / lowestCommonAncestor_other / lowestCommonAncestor_other2
#if 1
#pragma GCC optimize(3)
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("unroll-loops")
#endif
/**
* 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];
// 作者 凄临雨 superzmy
class Solution
{
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if constexpr ((Repeat) > 1)
{
volatile int count = (Repeat) - 1;
while (count)
count -= Implement(root, p, q) != NULL;
if (count != 0) throw(1);
}
auto ret = Implement(root, p, q);
if constexpr (RuinTree) // 破坏root子节点,造成内存泄露,这样leetcode退出程序时销毁内存时跳过这些节点直接导致内存从14MB→10MB
{
root->left = NULL;
root->right = NULL;
}
return ret;
}
static 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;
}
static 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;
}
static 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;
}
/
/
// 别人的实现,用于对比
/
/
static 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)
}
static TreeNode* lowestCommonAncestor_other2(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == NULL){
return root;
}
stack<TreeNode*> s1;
stack<TreeNode*> s2;
TreeNode* cur1 = root;
TreeNode* cur2 = root;
TreeNode* prePop1 = root;
TreeNode* prePop2 = root;
s1.push(root);
s2.push(root);
while(cur1 != p){
if(!cur1 -> left && !cur1 -> right){
s1.pop();
prePop1 = cur1;
cur1 = s1.top();
continue;
}
if(prePop1 == cur1 -> right || (prePop1 == cur1 -> left && cur1 -> right == NULL)){
s1.pop();
prePop1 = cur1;
cur1 = s1.top();
continue;
}
if(cur1 -> left == NULL || prePop1 == cur1 -> left){
if(cur1 -> right){
s1.push(cur1 -> right);
}
else{
s1.pop();
prePop1 = cur1;
}
cur1 = s1.top();
continue;
}
if(cur1 -> left){
s1.push(cur1 -> left);
}
cur1 = s1.top();
}
while(cur2 != q){
if(!cur2 -> left && !cur2 -> right){
s2.pop();
prePop2 = cur2;
cur2 = s2.top();
continue;
}
if(prePop2 == cur2 -> right || (prePop2 == cur2 -> left && cur2 -> right == NULL)){
s2.pop();
prePop2 = cur2;
cur2 = s2.top();
continue;
}
if(cur2 -> left == NULL || prePop2 == cur2 -> left){
if(cur2 -> right){
s2.push(cur2 -> right);
}
else{
s2.pop();
prePop2 = cur2;
}
cur2 = s2.top();
continue;
}
if(cur2 -> left){
s2.push(cur2 -> left);
}
cur2 = s2.top();
}
int l = s1.size() - s2.size();
if(l > 0){
while(l--){
s1.pop();
}
}
else{
l = -l;
while(l--){
s2.pop();
}
}
while(!s1.empty() && !s2.empty()){
if(s1.top() == s2.top()){
return s1.top();
}
else{
s1.pop();
s2.pop();
}
}
return root;
}
};
#if MyIO
int mymain() {
ofstream fout("user.out");
static char buff[400000];
size_t size = 0;
size = fread(buff, 1, sizeof(buff) - 1, stdin);
buff[size] = 0;
char* p = buff;
p++;
static TreeNode allNodes[12000];
while (true)
{
TreeNode* pNodeLast = allNodes;
int parentNodeIndex = 1;
int cur = *p++;
bool sign = false;
while (cur != ']')
{
if (cur == '-')
{
sign = true;
cur = *p++;
}
else if ('0' <= cur && cur <= '9')
{
int value = 0;
do
{
value = value * 10 + (cur - '0');
cur = *p++;
} while ('0' <= cur && cur <= '9');
TreeNode*& parentPChild = (&(allNodes[parentNodeIndex/2].left)) [parentNodeIndex&1];
parentPChild = ++pNodeLast;
pNodeLast->val = sign ? -value : value;
pNodeLast->left = NULL;
pNodeLast->right = NULL;
parentNodeIndex++;
sign = false;
}
else if (cur == 'n')
{
parentNodeIndex++;
cur = p[4];
p += 5;
}
else
cur = *p++;
}
if constexpr (0)
for (TreeNode* pNode = allNodes + 1; pNode <= pNodeLast; ++pNode)
printf("%d %d %d\n",
pNode->val,
pNode->left ? pNode->left->val : -1,
pNode->right ? pNode->right->val : -1);
int args[2] = {};
int* pargs = args;
TreeNode* pq[2] = {};
while (cur)
{
if (cur == '-')
{
sign = true;
cur = *p++;
}
else if ('0' <= cur && cur <= '9')
{
int value = 0;
do
{
value = value * 10 + (cur - '0');
cur = *p++;
} while ('0' <= cur && cur <= '9');
*pargs++ = sign ? -value : value;
sign = false;
if (pargs - args == 2)
break;
}
else
cur = *p++;
}
for (TreeNode* pNode = allNodes + 1; pNode <= pNodeLast; ++pNode)
{
if (pNode->val == args[0])
{
pq[0] = pNode;
if (pq[1])
break;
}
else if (pNode->val == args[1])
{
pq[1] = pNode;
if (pq[0])
break;
}
}
auto ret = Solution().lowestCommonAncestor(&allNodes[1], pq[0], pq[1])->val;
fout << ret << endl;
for(cur = *p++; cur != '['; cur = *p++)
if (cur == 0)
return 0;
}
return 0;
};
int main() {
return mymain();
}
#define main main2
#endif
作者:凄临雨 zmy
链接: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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。