PAT甲级1151 LCA in a Binary Tree (30point(s))

 Seeking LCA, of course, there is no need to write a real LCA algorithm (tarjan or something). The topic gives the middle-order traversal and pre-order traversal of the tree. We don't need to build the tree first, but can find the LCA of the two nodes in the process of building the tree.

The specific idea is that when two nodes are different, they must be distributed on both sides of their LCA, combined with the given in-order traversal, the nodes distributed on both sides are relative to the position of the root node in the in-order traversal It happens to be one after another. According to this property, we can follow the method of tree building, first find the root in the pre-order traversal, and then find the two sub-trees in the middle-order traversal to see if the two nodes are distributed on two different sub-trees . The search is repeated until a satisfactory situation is found. The first time that the two nodes are distributed on two different subtrees, the root node is the LCA you are looking for.

The method to determine whether they are on two different subtrees is to calculate the difference between their subscripts and the subscripts of the current root node. As mentioned earlier, when LCA is found, the position of the two nodes in the middle order traversal must be one after the root node. At this time, subtract the subscripts of the two nodes from the subscripts of the root node respectively, and the product of the two differences must be negative. If the product is 0, it means that the subscript of one of the nodes is the same as the root node, indicating that they are the same node, then their LCA is this node. If the product is positive, it means they are on the same side and need to continue.

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e4+4;
int m, n;
int pre[maxn], in[maxn];
map<int, int> inid;

void read() {
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &in[i]);
        inid[in[i]] = i; //先保存各个结点在inorder下的下标,加速
    }
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &pre[i]);
    }
}

void find(int u, int v, int rid) {
    if (rid > n) return; //边界处理

    int rt = pre[rid]; //当前检查的根
    int rpos = inid[rt]; //根在inorder下的下标
    int udif = INF, vdif = INF; //udif、vdif分别为u、v与根的下标的差值
    if (inid.count(u)) udif = rpos - inid[u]; //如果存在,计算差值
    if (inid.count(v)) vdif = rpos - inid[v]; //如果不存在,值为默认的INF

    if (udif == INF && vdif != INF) { //先检查u、v是否存在
        printf("ERROR: %d is not found.\n", u);
    } else if (udif != INF && vdif == INF) {
        printf("ERROR: %d is not found.\n", v);
    } else if (udif == INF && vdif == INF) {
        printf("ERROR: %d and %d are not found.\n", u, v);
    } else if (udif*vdif < 0) { //如果都存在,且差值的乘积为负数,说明在根的两侧
        printf("LCA of %d and %d is %d.\n", u, v, rt);
    } else if (udif*vdif == 0) { //如果为0,说明其中一个与根相等
        printf("%d is an ancestor of %d.\n", rt, u == rt ? v : u);
    } else if (udif*vdif > 0) { //如果为正数,说明在根的同一侧,继续分析
        find(u, v, rid+1); //写了个尾递归。。可以改成循环
    }
}

void solve() {
    while (m--) {
        int u, v;
        scanf("%d%d", &u, &v);
        find(u, v, 1);
    }
}

int main() {
    read();
    solve();
    return 0;
}

 

Guess you like

Origin blog.csdn.net/HNUCSEE_LJK/article/details/108218965