PAT甲级1151 LCA in a Binary Tree、1143 Lowest Common Ancestor最小公共节点

这篇博客探讨了一种高效的方法,通过先序和中序遍历序列来查找二叉树中两个节点的最低公共祖先(LCA)。文章详细解释了算法思路,包括如何根据输入序列构建唯一的二叉树,以及如何在不构建树的情况下进行查询。主要优化在于避免在每次判断时查询映射表,从而减少超时可能性。此外,还提供了两种不同场景下的LCA查找策略,一种针对任意二叉树,另一种针对二叉搜索树。
摘要由CSDN通过智能技术生成

PAT甲级1151 LCA in a Binary Tree

The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U and V as descendants.

Given any two nodes in a binary tree, you are supposed to find their LCA.

Input Specification:
Each input file contains one test case. For each case, the first line gives two positive integers: M (1,000), the number of pairs of nodes to be tested; and N (10,000), the number of keys in the binary tree, respectively. In each of the following two lines, N distinct integers are given as the inorder and preorder traversal sequences of the binary tree, respectively. It is guaranteed that the binary tree can be uniquely determined by the input sequences. Then M lines follow, each contains a pair of integer keys U and V. All the keys are in the range of int.

Output Specification:
For each given pair of U and V, print in a line LCA of U and V is A. if the LCA is found and A is the key. But if A is one of U and V, print X is an ancestor of Y. where X is A and Y is the other node. If U or V is not found in the binary tree, print in a line ERROR: U is not found. or ERROR: V is not found. or ERROR: U and V are not found..

Sample Input:
6 8
7 2 3 4 6 5 1 8
5 3 7 2 6 4 8 1
2 6
8 1
7 9
12 -3
0 8
99 99
结尾无空行
Sample Output:
LCA of 2 and 6 is 3.
8 is an ancestor of 1.
ERROR: 9 is not found.
ERROR: 12 and -3 are not found.
ERROR: 0 is not found.
ERROR: 99 and 99 are not found.
结尾无空行

柳神

下面代码基本是柳神代码修改而来

这一题的主要思想是不需要构建树,只需要根据先序和中序确定的唯一树进行查询即可

假设查询数字为a和b,使用先序遍历查询,有以下情况:
1、a或b不出现在树中,输出error
2、a和b在树中的所在位置为根节点的左右子树,那么此时最小公共祖先节点就是该根节点
3、a或b为此时的根节点,那么此时a或b为另一个节点的最小公共节点
4、a和b位于根节点的左子树,那么就将根节点设置为左子树根节点,进行递归
5、a和b位于根节点的右子树,那么就将根节点设置为右子树根节点,进行递归

这里主要是超时问题,测试点4会出现超时,出错原因:
lca函数中需要提前将a,b,root在中序所在位置存入局部变量,然后在条件中使用这些局部变量,而不是每进行一次判断就在map里进行查询,这样会超时

这一题其实不管是先序遍历还是中序遍历查询(例如是柳神代码里的)都是可以通过的,超时原因不在于这里

思想

根据先序序列和中序序列可以得到一颗唯一的二叉树,中序序列是左中右,因此只要判断查询节点是否位于中序序列的根节点两边即可

步骤

1、记录中序序列和先序序列,记录每个值对应所在中序的位置
2、lca中判断当前a和b所在中序的位置,是否下标大于小于当前树的根节点,又或是等于
3、如果不是,那就看是位于左子树还是右子树,然后在各自子树找

#include <iostream>
#include <vector>
#include <map>
using namespace std;
vector<int> pre;
vector<int> in;
map<int, int> pos_in;
int flag = 0;
void lca(int a, int b, int s1, int e1, int s2, int e2) {
    if (s1 > e1 || s2 > e2) {
        return;
    }  
    int a_pos = pos_in[a], b_pos = pos_in[b], rootidx = pos_in[pre[s1]];
    if ((a_pos < rootidx && b_pos > rootidx) || (a_pos > rootidx && b_pos < rootidx)) {
        printf("LCA of %d and %d is %d.\n", a, b, pre[s1]);
        return;
    }
    else if (a_pos == rootidx) {
        printf("%d is an ancestor of %d.\n", a, b);
        return;
    }
    else if (b_pos == rootidx) {
        printf("%d is an ancestor of %d.\n", b, a);
        return;
    }
    else if (a_pos < rootidx && b_pos < rootidx) {
        lca(a, b, s1 + 1, rootidx - s2 + s1, s2, rootidx - 1);
    }
    else if (a_pos > rootidx && b_pos > rootidx) {
        lca(a, b, rootidx - s2 + s1 + 1, e1, rootidx + 1, e2);
    }
}
int main() {
    int m, n;
    cin >> m >> n;
    pre.resize(n);
    in.resize(n);
    for (int i = 0; i < n; ++i) {
        cin >> in[i];
        pos_in[in[i]] = i;
    }
    for (int i = 0; i < n; ++i) {
        cin >> pre[i];
    }
    for (int i = 0; i < m; ++i) {
        int a, b;
        cin >> a >> b;
        flag = 0;
        if (pos_in.find(a) == pos_in.end() && pos_in.find(b) == pos_in.end()) {
            printf("ERROR: %d and %d are not found.\n", a, b);
        }
        else if (pos_in.find(a) == pos_in.end()) {
            printf("ERROR: %d is not found.\n", a);
        }
        else if (pos_in.find(b) == pos_in.end()) {
            printf("ERROR: %d is not found.\n", b);
        }
        else {
            lca(a, b, 0, n - 1, 0, n - 1);
        }
    }
    return 0;
}

1143 Lowest Common Ancestor

先序遍历确定一颗唯一的二叉搜索树

主要思路和上面类似,只不过是从位置大小转变为数值大小:
1、a或b不出现在树中,输出error
2、a大于根节点且b小于根节点,或a小于根节点且b大于根节点(其实意思就是a和b在二叉搜索树的根节点的两侧),那么此时最小公共祖先节点就是该根节点
3、a或b为此时的根节点,那么此时a或b为另一个节点的最小公共节点
4、其余情况则继续遍历先序序列中的下一个数

#include <iostream>
#include <vector>
#include <map>
using namespace std;
vector<int> pre;
map<int,int> pos;
int n;
void lca(int a,int b,int s1){
    if((a < pre[s1]&&b > pre[s1])||(a > pre[s1]&&b < pre[s1])){
        printf("LCA of %d and %d is %d.\n",a,b,pre[s1]);
    }
    else if(a == pre[s1]){
        printf("%d is an ancestor of %d.\n",a,b);
    }
    else if(b == pre[s1]){
        printf("%d is an ancestor of %d.\n",b,a);
    }
    else{
        lca(a,b,s1 + 1);
    }
}
int main(){
    int m;
    cin >> m >> n;
    pre.resize(n);
    for(int i = 0;i < n;++i){
        cin >> pre[i];
        pos[pre[i]] = i;
    }
    for(int i = 0;i < m;++i){
        int a,b;
        cin >> a >> b;
        if(pos.find(a) == pos.end()&&pos.find(b) == pos.end()){
            printf("ERROR: %d and %d are not found.\n",a,b);
        }
        else if(pos.find(a) == pos.end()){
            printf("ERROR: %d is not found.\n",a);
        }
        else if(pos.find(b) == pos.end()){
            printf("ERROR: %d is not found.\n",b);
        }
        else{
            lca(a,b,0);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值