1143 Lowest Common Ancestor

题意:给出BST的前序序列,以及若干个查询。寻找BST中任意给定两个结点u,v的最低公共祖先(LCA)。

思路:
1、首先根据前序序列和中序序列构建好二叉搜索树(思考1:此处明明是BST,为何不在输入前序序列的过程中直接插入建树?这样做岂不是更方便?分析见最后)
2、利用二叉搜索树的性质寻找结点u和v的最低公共祖先(递归解法)(思考2:如果不是二叉搜索树,而是普通的二叉树,该如何寻找LCA?分析见最后)
    1)如果根结点的值大于max(u,v),说明u和v均在根结点的左子树,则进入根结点的左子结点继续递归
    2)如果根结点的值小于min(u,v),说明u和v均在根结点的右子树,则进入根结点的右子结点继续递归
    3)剩下的情况就是,根结点的值比其中一个值大,比另外一个小,则该结点就是结点u,v的最低公共祖先了
核心代码:
//获取BST树的结点u,v的最低公共祖先
Node* getLCA(Node* root,int u,int v)
{
    if(root==nullptr) return nullptr;
    if(root->val > max(u,v)) return getLCA(root->lchild,u,v);
    else if(root->val < min(u,v)) return getLCA(root->rchild,u,v);
    else return root;
}
3、由于题目中查找的元素有可能不是树的结点,且"All the keys are in the range of  int",即结点的值可能为负,因此,额外设置一个map<int,init> mp,用于记录被查询的值是否出现过。不过,最好是使用  unordered_map ,头文件<unordered_map>,因为这里只是作为一个hash映射,不需要key值的顺序关系,用unordered_map效率更高!
 
代码:
#include <cstdio>
#include <unordered_map>
#include <vector>
#include <algorithm>
using namespace std;

struct Node{
    int val;
    Node *lchild,*rchild;
    Node(int v_):val(v_),lchild(nullptr),rchild(nullptr){}
};

Node* buildBiTree(vector<int>& preOrder,int preL,int preR,vector<int>& inOrder,int inL,int inR)
{
    if(preL>preR || inL>inR) return nullptr;
    int rootVal=preOrder[preL];
    Node* root=new Node(rootVal);
    int pos;
    for(pos=inL;pos<=inR;pos++)
        if(inOrder[pos]==rootVal) break;
    int leftCnt=pos-inL;
    root->lchild=buildBiTree(preOrder,preL+1,preL+leftCnt,inOrder,inL,pos-1);
    root->rchild=buildBiTree(preOrder,preL+leftCnt+1,preR,inOrder,pos+1,inR);
    return root;
}

//获取BST树的结点u,v的最低公共祖先
Node* getLCA(Node* root,int u,int v)
{
    if(root==nullptr) return nullptr;
    if(root->val > max(u,v)) return getLCA(root->lchild,u,v);
    else if(root->val < min(u,v)) return getLCA(root->rchild,u,v);
    else return root;
}

int main()
{
    //freopen("pat.txt","r",stdin);
    int queryCnt,n;
    scanf("%d%d",&queryCnt,&n);
    vector<int> preOrder(n),inOrder(n);
    unordered_map<int,int> mp;//用来判断查询值是否为树的结点
    for(int i=0;i<n;i++){
        scanf("%d",&preOrder[i]);
        mp[preOrder[i]]=1;
    }
    inOrder=preOrder;
    sort(inOrder.begin(),inOrder.end());
    Node* root=buildBiTree(preOrder,0,n-1,inOrder,0,n-1);
    int u,v;
    for(int k=0;k<queryCnt;k++){
        scanf("%d%d",&u,&v);
        if(mp[u]==0 && mp[v]!=0)
            printf("ERROR: %d is not found.\n",u);
        else if(mp[u]!=0 && mp[v]==0)
            printf("ERROR: %d is not found.\n",v);
        else if(mp[u]==0 && mp[v]==0)
            printf("ERROR: %d and %d are not found.\n",u,v);
        else{
            Node* pNode=getLCA(root,u,v);
            int anc=pNode->val;
            if(anc==u) printf("%d is an ancestor of %d.\n",u,v);
            else if(anc==v) printf("%d is an ancestor of %d.\n",v,u);
            else printf("LCA of %d and %d is %d.\n",u,v,anc);
        }
    }
    return 0;
}

【分析1】

如果直接在输入数据的时候插入建树,如下,会有两个测试点超时。为什么?

void insert(Node* &root,int val)
{
    if(root==nullptr){
        root=new Node(val);
        return;
    }
    if(val<root->val) insert(root->lchild,val);
    else insert(root->rchild,val);
}

for(int i=0;i<n;i++){
    scanf("%d",&val);
    insert(root,val);
    mp[val]=1;
}

当BST为极端情况时(即形成单链表的情况),若按照插入直接建树。i=0时,无需递归;i=1时,insert()递归1次,i=2时,insert()递归2次,...,i=n-1时,insert()递归n-1次,时间是O(1+2+...+n)=O(n^2),这里n最大为10000,因此显然超时。

而利用【前序+中序-->二叉树】的方法,即使在极端情况下建树的时间复杂度也是O(n)。

【分析2】
若树为普通的二叉树,如何寻找任意两结点u,v的最低公共祖先?
1、递归解法(参考 这里
在递归函数中,我们首先判断当前结点是否为空,若为空,直接返回;或者,当前结点是否就是u或v,若是,也直接返回该结点。否则,就对其左右孩子结点分别调用递归函数。可以想到,结点u和v有以下三种情况:
  1)u和v分别位于当前结点的左右两侧;
  2)u和v都在当前结点的左子树中;
  3)u和v都在当前结点的右子树中。

若u和v分别位于左右子树中,那么对左右子结点调用递归函数,会分别返回u和v结点的位置,而当前结点正好就是u和v的最小共同父结点,直接返回当前结点即可。

若u和v同时位于左子树,这里有两种情况,一种情况是left会返回u和v中较高的那个位置,而right会返回空,所以我们最终返回非空的left即可;还有一种情况是会返回u和v的最小父结点,就是说当前结点的左子树中的某个结点才是u和v的最小父结点,会被返回。

若u和v同时位于右子树,同样这里有两种情况,一种情况是right会返回u和v中较高的那个位置,而left会返回空,所以我们最终返回非空的right即可,还有一种情况是会返回u和v的最小父结点,就是说当前结点的右子树中的某个结点才是u和v的最小父结点,会被返回。

代码如下:

Node* getLCA(Node* root,int u,int v)
{
    if(root==nullptr || root->val==u || root->val==v) return root;
    Node* left=getLCA(root->lchild,u,v);
    Node* right=getLCA(root->rchild,u,v);
    if(left && right) return root;
    else return (left?left:right);
}

2、非递归解法

我在这部分内容中讲到过后序遍历的非递归写法,这种写法有个性质就是,“当访问到结点v时,栈中存放的元素恰为结点v的所有父节点”。由此,可以想到,为了寻找结点u和v的最低公共祖先,可以非递归后序遍历该二叉树,当访问到结点u时,当前栈中的元素就是结点u的所有父节点(从下至上的),把栈中的元素拷贝至ancestor_u中;然后,继续访问,当访问到当访问到结点v时,当前栈中的元素就是结点v的所有父节点(从下至上的),把栈中的元素拷贝至ancestor_v中。找出ancestor_u和ancestor_v中的首个相同的元素,就是两者的LCA。不过,因为ancestor_u和ancestor_v中的元素是无序的,因此,这一步的时间复杂度O(n^2),因此,这个方法是不推荐的。只是了解一下。

  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

转载于:https://www.cnblogs.com/kkmjy/p/9529771.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是C#中二叉树的lowest common ancestor的源代码: ```csharp using System; public class Node { public int value; public Node left; public Node right; public Node(int value) { this.value = value; this.left = null; this.right = null; } } public class BinaryTree { public Node root; public BinaryTree() { this.root = null; } public Node LowestCommonAncestor(Node node, int value1, int value2) { if (node == null) { return null; } if (node.value == value1 || node.value == value2) { return node; } Node left = LowestCommonAncestor(node.left, value1, value2); Node right = LowestCommonAncestor(node.right, value1, value2); if (left != null && right != null) { return node; } return (left != null) ? left : right; } } public class Program { public static void Main() { BinaryTree tree = new BinaryTree(); tree.root = new Node(1); tree.root.left = new Node(2); tree.root.right = new Node(3); tree.root.left.left = new Node(4); tree.root.left.right = new Node(5); tree.root.right.left = new Node(6); tree.root.right.right = new Node(7); Node lca = tree.LowestCommonAncestor(tree.root, 4, 5); Console.WriteLine("Lowest Common Ancestor of 4 and 5: " + lca.value); lca = tree.LowestCommonAncestor(tree.root, 4, 6); Console.WriteLine("Lowest Common Ancestor of 4 and 6: " + lca.value); lca = tree.LowestCommonAncestor(tree.root, 3, 4); Console.WriteLine("Lowest Common Ancestor of 3 and 4: " + lca.value); lca = tree.LowestCommonAncestor(tree.root, 2, 4); Console.WriteLine("Lowest Common Ancestor of 2 and 4: " + lca.value); } } ``` 在上面的代码中,我们定义了一个Node类和一个BinaryTree类。我们使用BinaryTree类来创建二叉树,并实现了一个LowestCommonAncestor方法来计算二叉树中给定两个节点的最近公共祖先。 在LowestCommonAncestor方法中,我们首先检查给定节点是否为null或与给定值之一匹配。如果是,则返回该节点。否则,我们递归地在左子树和右子树上调用LowestCommonAncestor方法,并检查它们的返回值。如果左子树和右子树的返回值都不为null,则当前节点是它们的最近公共祖先。否则,我们返回非null的那个子树的返回值。 在Main方法中,我们创建了一个二叉树,并测试了LowestCommonAncestor方法的几个不同输入。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值