18年秋季第四题 PAT甲级 1151 LCA in a Binary Tree (30分)

题目来源:https://pintia.cn/problem-sets/994805342720868352/problems/1038430130011897856

备考汇总贴:2020年3月PAT甲级满分必备刷题技巧

题目

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.

题目大意

给定中序、前序遍历序列,找最近公共祖先,相当于找两个子辈在族谱里面辈分最近的共同祖先。

易疏忽点分析

1.输出有几种?
题目写出的有四种:两个都找不到、其中一个找不到、其中一个是LCA、有LCA。
但是还有个隐藏的边界:如果给出的两个数是相同的,是属于“其中一个是LCA”。这个知识点是学习LCA的必须掌握的。

2.要解出LCA最近共同祖先,应该先从递归的角度思考LCA定义。

解题方法

这道题有两个思路:

第一个思路就是递归地看两个值在root的哪边:

  • 两个值都在左边,则LCA在左边
  • 两个值都在右边,则LCA在右边
  • 一个在左一个在右,则说明LCA就是当前的root节点
  • 其余的情况只能是一个正好是另一个的根节点,则LCA就是这个root节点

第一个思路的满分代码(从liuchuo博客复制,仅做了语句的排版)

#include <iostream>
#include <vector>
#include <map>
using namespace std;
map<int, int> pos;
vector<int> in, pre;
void lca(int inl, int inr, int preRoot, int a, int b) {
    if (inl > inr) return;
    int inRoot = pos[pre[preRoot]], aIn = pos[a], bIn = pos[b];
    if (aIn < inRoot && bIn < inRoot)//两个值都在左边,则LCA在左边
        lca(inl, inRoot-1, preRoot+1, a, b);
    else if (aIn > inRoot && bIn > inRoot)//两个值都在右边,则LCA在右边
        lca(inRoot+1, inr, preRoot+1+(inRoot-inl), a, b);
    else if ((aIn < inRoot && bIn > inRoot) || (aIn > inRoot && bIn < inRoot))//一个在左一个在右,则说明LCA就是当前的root节点
        printf("LCA of %d and %d is %d.\n", a, b, in[inRoot]);
    else if (aIn == inRoot)
        printf("%d is an ancestor of %d.\n", a, b);
    else if (bIn == inRoot)
        printf("%d is an ancestor of %d.\n", b, a);
}
int main() {
    int m, n, a, b;
    scanf("%d %d", &m, &n);
    in.resize(n + 1);
    pre.resize(n + 1);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &in[i]);
        pos[in[i]] = i;
    }
    for (int i = 1; i <= n; i++) scanf("%d", &pre[i]);
    for (int i = 0; i < m; i++) {
        scanf("%d %d", &a, &b);
        if (pos[a] == 0 && pos[b] == 0)
            printf("ERROR: %d and %d are not found.\n", a, b);
        else if (pos[a] == 0 || pos[b] == 0)
            printf("ERROR: %d is not found.\n", pos[a] == 0 ? a : b);
        else
            lca(1, n, 1, a, b);
    }
    return 0;
}

第二种思路则需要建树+标记节点的父节点:

1.根据先序和中序确定每个结点的父亲结点及层数;
2.将低的层次的点回溯到对应高层次的祖先结点,再依据值进行判断或回溯。

第二种思路满分代码(代码转载自csdn网友

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
#define MAX 10005
int pre[MAX],in[MAX];
typedef struct node{
    int lev,par;
}node;
map<int,node>arr;//int 的高度和父亲
void CreatTree(int inL,int inR,int preL,node no)//中序先序建后序
{
    if(inL>inR)return;
    int i=inL;
    while(in[i]!=pre[preL])++i;//中序中根节点位置
    arr[pre[preL]]=no;
    ++no.lev;no.par=pre[preL];
    CreatTree(inL,i-1,preL+1,no);
    CreatTree(i+1,inR,preL+i-inL+1,no);//先序中右子树根节点为  当前根节点位置+左子树个数+1
}
int main()
{
    freopen("test.txt","r",stdin);
    int i,N,M;
    scanf("%d %d",&M,&N);
    for(i=0;i<N;++i)scanf("%d",&in[i]);
    for(i=0;i<N;++i)scanf("%d",&pre[i]);
    node no;no.lev=0;no.par=-1;
    CreatTree(0,N-1,0,no);
    while(M--){
        int a,b;
        scanf("%d %d",&a,&b);
        if(!arr.count(a)&&!arr.count(b))printf("ERROR: %d and %d are not found.\n",a,b);//没有a,b结点
        else if(!arr.count(a)||!arr.count(b))printf("ERROR: %d is not found.\n",arr.count(a)?b:a);//没有其中一个结点
        else if(a==b)printf("%d is an ancestor of %d.\n",a,b);//同一结点
        else{
            int x=a,y=b;
            while(arr[a].lev<arr[b].lev)b=arr[b].par;//b回溯到与a同一层,
            if(b==a){printf("%d is an ancestor of %d.\n",a,y);continue;}//a是b祖先
            while(arr[b].lev<arr[a].lev)a=arr[a].par;//a回溯到与b同一层
            if(b==a){printf("%d is an ancestor of %d.\n",b,x);continue;}//b是a祖先
            while(b!=a){a=arr[a].par;b=arr[b].par;}//一起回溯。
            printf("LCA of %d and %d is %d.\n",x,y,a);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/allisonshing/article/details/104570744