PAT1151 分析

本文分析了PAT1151题目的解决方案,首先尝试根据前序和中序序列构建并遍历二叉树,但因时间复杂度过高导致超时。接着,通过优化存储每个节点的路径来减少时间复杂度,但空间不足。最终,作者发现不需要构建树,仅使用前序和中序序列的位置信息就能确定最近公共祖先。关键在于找到在中序序列中位于U和V之间的节点,它一定是最近祖先。代码实现中需要注意先判断U和V是否存在。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1,题目

点这里->

2,思路分析

  • 一开始想着根据pre和in序列先构建一颗bt,然后使用dfs遍历到对应的结点时,存储下路径,这个路径上的点就是U的所有祖先,同理找出U和V的祖先后“由后往前”就可以找到最近的祖先结点。

  • 结果:。。。尴尬了,最后两个点超时,想了下,结点范围10000,最坏情况下必然超时。

  • 改进1:用空间换时间,实现把所有结点的路径存储下来,这样只需要一遍dfs就可以把所有的结点的祖先找出来,然后同样的方法找出最近的祖先即可,结果。。。又尴尬了,空间不够。。。

  • 最后:苦思之前有一题的思路,结点U和V的祖先一定在pre序列的左边(注意包含U和V中前序序列考前的元素,因为可能U是V的祖先,或者反之),同时也一定在inorder序列的中间(同时包含U和V的中序序列,道理同前)。这样一来,不需要建树就可以找出最近祖先

  • 步骤:

  • 1,输入序列时同时用map分别记录各个元素在preorder和inorder中的位置,为之后判断元素在U、V的前或中间提供便捷。

	for(int i=0; i<N; ++i) {
        scanf("%d",&insequence[i]);
        allNum.insert(insequence[i]);
        in_value_pos[insequence[i]]=i;
    }
    for(int i=0; i<N; ++i) {
        scanf("%d",&presequence[i]);
        pre_value_pos[presequence[i]]=i;
    }
  • 注意,allNum时一个set,记录出现的所有元素。

  • 2,当输入了U,V后,通过前边的记录,分别找到U,V的preorder中靠左(靠前)的元素(从左到右!!!很重要,后边说明!)从左到右遍历,只要该元素在U,V的inorder序列的中间,就说明其是最近祖先!
    举例:
    如图:
    在这里插入图片描述
    前序序列:2,3,7,10,6,4,12,13,1,8,11,14,9,5
    中序序列:10,7,6,3,12,4,13,2,11,8,14,1,9,5

  • 例子1:
    U=14,V=5
    可见从前序序列,应从前序序列元素2–14依次判断,只有1符合在中序序列中,1处于14和5之间。

  • 例子2:U=1,V=5
    同理可得,1为最近祖先

- 关键分析:
为什么这样找到的一定是最近祖先??
1,结点的祖先一定在前序序列的左方(包含自身)
2,两节点的祖先一定在中序序列之间(包含两结点)
**3,这是最重要的一点,中序序列中,结点U和V之间的结点只能包含其最近的一个祖先和其他非祖先结点。 **
由此,找到的一定是最近祖先。

3,关键点

  • 最近祖先:中序序列之间的结点只包含最近的一个祖先

4,注意点

  • 注意先判断结点U和V是否在树中,这也是set的作用。

5,代码

#include<stdio.h>
#include<set>
#include<algorithm>
#include<map>
using namespace std;
const int Maxn=10010;
int insequence[Maxn],presequence[Maxn];
map<int,int> in_value_pos,pre_value_pos;
int main() {
    int M,N;
    scanf("%d %d",&M,&N);
    set<int> allNum;
    for(int i=0; i<N; ++i) {
        scanf("%d",&insequence[i]);
        allNum.insert(insequence[i]);
        in_value_pos[insequence[i]]=i;
    }
    for(int i=0; i<N; ++i) {
        scanf("%d",&presequence[i]);
        pre_value_pos[presequence[i]]=i;
    }
    int U,V;
    for(int i=0; i<M; ++i) {
        scanf("%d %d",&U,&V);
        if(allNum.find(U)!=allNum.end()) {
            if(allNum.find(V)!=allNum.end()) {
                int left_pos,u_pos=pre_value_pos[U],v_pos=pre_value_pos[V];
                left_pos=min(u_pos,v_pos);
                for(int j=0; j<=left_pos; ++j) {
                    int test_pos=in_value_pos[presequence[j]];
                    int u_pos_in=in_value_pos[U],v_pos_in=in_value_pos[V];
                    if(test_pos>=min(u_pos_in,v_pos_in)&&test_pos<=max(u_pos_in,v_pos_in)) {
                        if(test_pos!=u_pos_in&&test_pos!=v_pos_in)
                            printf("LCA of %d and %d is %d.\n",U,V,insequence[test_pos]);
                        else {
                            if(test_pos==u_pos_in)
                                printf("%d is an ancestor of %d.\n",U,V);
                            else
                                printf("%d is an ancestor of %d.\n",V,U);
                        }
                        break;
                    }
                }
            } else {
                printf("ERROR: %d is not found.\n",V);
            }
        } else {
            if(allNum.find(V)!=allNum.end()) {
                printf("ERROR: %d is not found.\n",U);
            } else {
                printf("ERROR: %d and %d are not found.\n",U,V);
            }
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值