第五章 树

1004 .数叶子结点

在这里插入图片描述

  • 把他输入的节点编号01,02当做1,2,3…就可以了
  • 然后定义邻接表存储树,dfs每个层次,用cnt[N]记录叶子结点max_depth记录最大深度,最后输出即可

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=110;

int h[N],e[N],ne[N],idx;

int cnt[N];

int n,m,max_depth;

void add(int a,int b){
    
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
    
}

void dfs(int root,int u){
    
    if(h[root]==-1){                      //如果没有子节点,则说明是叶子结点,当前层次叶子结点++
        
        cnt[u]++;
        
        max_depth=max(u,max_depth);
        
        return ;
    }
    
        
    for(int i=h[root];i!=-1;i=ne[i]){          //遍历当前根节点的每一个儿子
        
        int root =e[i];
        
        dfs(root,u+1);
    }
}

int main(){
    
    memset(h,-1,sizeof(h));                //初始化邻接表
    
    cin>>n>>m;
    
    while(m--){
        
        int id;
        
        int k;
        
        cin>>id>>k;
        
        while(k--){
            
            int son;
            
            cin>>son;
            
            add(id,son);
        }
    }
    
    dfs(1,0);
    
    cout<<cnt[0];
    
    for(int i=1;i<=max_depth;i++)cout<<" "<<cnt[i];
}

1020.树的遍历

在这里插入图片描述

  • 思路是直接根据后序和中序遍历构造新的二叉树,在层序遍历输出即可
  • 此题写法是直接用数组存储左右孩子节点,写法上简单不少(船新写法!!!!)
  • 由于pat卡输出空格,最后bfs时手动模拟队列最后一次性输出会比较简单
#include<iostream>
#include<algorithm>
#include<queue>

using namespace std;

const int N=50;

int pos[N],ino[N];

unordered_map<int ,int> l,r,loc;        //存储左右孩子,loc存储节点val在中序遍历中的位置

int build(int pl,int pr,int il,int ir){    //pl,pr,对应后序遍历左右边界,il,ir对应中序遍历左右边界

    int root=pos[pr];
    
    int k=loc[root];
    
    int len=k-il;
    
    if(il<k)l[root]=build(pl,pl+len-1,il,k-1);     //如果中序遍历中存在左(右)子树,则递归的建立左右孩子节点
    
    if(k<ir)r[root]=build(pl+len,pr-1,k+1,ir);
    
    return root;
}

int q[N];

void bfs(int root){
    
    int hh=-1,tt=0;
    
    q[++hh]=root;
    
    while(hh>=tt){
        
        int it=q[tt++];
        
        if(l[it])q[++hh]=l[it];
        
        if(r[it])q[++hh]=r[it];
    }
    
    cout<<q[0];
    
    for(int i=1;i<tt;i++)cout<<" "<<q[i];
    
    return;
}

int main(){
    
    int n;
    
    cin>>n;
    
    for(int i=1;i<=n;i++)cin>>pos[i];
    
    for(int i=1;i<=n;i++){
        
        cin>>ino[i];
        
        loc[ino[i]]=i;                 //顺便保存val值在中序遍历中的位置
    }
    
    int root =build(1,n,1,n);          
    
    bfs(root);
}

1021.最深的根 ** (并查集判断是否只有一个连通块)

在这里插入图片描述

  • 一个树最多只有N-1条边,那么按照题意,如果N-1条边要出现连通图的话,一定存在孤立节点
  • 因为只有N-1条边,所以不需要复杂的判环操作,只需要使用并查集查看所有点是否都连通在一个子集当中即可
  • 注意,由于题目中没有给定边的方向,所以我们全部存取双向边
  • pps:又由于本题我们存取了双向边,所以在dfs时有可能会存在死循环搜索的情况,因此要多加一特判条件

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
#include<map>

using namespace std;

const int N=10010,M=2*N;

int h[N],e[M],ne[M],idx;         //存取双向边

int p[N];

int n;

void add(int a,int b){
    
    e[idx]=b;
    
    ne[idx]=h[a];
    
    h[a]=idx++;
}

int find(int x){
    
    if(p[x]!=x)p[x]=find(p[x]);
    
    return p[x];
}

int dfs(int root,int father){
    
    int lenth=0;
    
    for(int i=h[root];i!=-1;i=ne[i]){
        
        int j=e[i];
        
        if(j!=father)lenth=max(lenth,dfs(j,root));     //添加一father节点判断是否产生死循环
    }
    
    return lenth+1;
}

int main(){

    cin>>n;
    
    memset(h,-1,sizeof h);
    
    int cnt=n;
    
    for(int i=1;i<=n;i++)p[i]=i;
    
    for(int i=0;i<n-1;i++){
        
        int a,b;
        
        cin>>a>>b;
        
        add(a,b),add(b,a);
        
        if(find(a)!=find(b)){
            
            p[find(a)]=find(b);
            
            cnt--;                     //如果两个节点没有连通,则将他们连通,并且连通块的数量-1
        }
    }
    
    if(cnt>1)printf("Error: %d components",cnt);    //如果最后连通块的数量只有一个,说明是一个合法的树,否则存在多余连通块
    
    else {
        
        vector<int> nodes;
        
        int max_depth=-1;
        
        for(int i=1;i<=n;i++){             //挨个当做根节点,dfs求其深度
            
            int depth=dfs(i,-1);
            
            if(depth>max_depth){          //如果找到跟深的点,清空vector并添加,更新深度
                
                nodes.clear();
                
                nodes.push_back(i);
                
                max_depth=depth;
            }
            
            else if (depth==max_depth)nodes.push_back(i);     //如果相等则直接加入vector
        }
        
        for(int i=0;i<nodes.size();i++)cout<<nodes[i]<<endl;
    }
}

1043.判断二叉搜索树 **

在这里插入图片描述

  • 如果是二叉排序树,那么输出的中序遍历一定是有序的,所以本题翻译为给定一个前序序列和中序序列,看是否能构成一颗二叉排序树
  • 并且注意本题条件,题目要求左子树的值一定小于根节点,右子树的值大于等于根节点,所以当存在相同值时,中序序列该节点的位置是靠前的那一个,镜像二叉排序树由于中序从大到小,所以与此情况相反
  • 总结:本题只需分别判断是否能构成二叉排序树及其镜像,若都不能输出NO即可
  • 注意: 本题不去建立树,直接通过前序及中序序列递归得到后序序列即可
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=10010;

int pre[N],ino[N];    //前序遍历,中序遍历

int post[N],cnt;           //后序遍历,后序遍历计数;

int n;

bool build(int pl,int pr,int il,int ir,int type){
    
    int root=pre[pl];
    
    int k;                               //找到先序遍历在中序遍历中对应点的位置,如果找不到直接返回false,说明不符合条件
    
    if(!type){
        
        for(k=il;k<=ir;k++)if(ino[k]==root)break;
        
        if(k>ir)return false;
    }
    
    else {
        
        for(k=ir;k>=il;k--)if(ino[k]==root)break;
        
        if(k<il)return false;
    }
    
    int len=k-il;
    
    bool check=true;
    
    if(il<k)check=build(pl+1,pl+len,il,k-1,type);      //检查左右子树是否可以建立
    
    if(ir>k)check=build(pl+len+1,pr,k+1,ir,type);
    
    post[cnt++]=root;
    
    return check;
}



int main(){
    
    cin>>n;
    
    for(int i=0;i<n;i++){
        
        cin>>pre[i];
        
        ino[i]=pre[i];
    }
    
    sort(ino,ino+n);                  //得到有序的中序序列
    
    if(build(0,n-1,0,n-1,0)){
        
        cout<<"YES"<<endl;
        
        cout<<post[0];
        
        for(int i=1;i<n;i++)cout<<" "<<post[i];
    }
    
    else {
        
        reverse(ino,ino+n);
        
        cnt=0;
        
        if(build(0,n-1,0,n-1,1)){
            
            cout<<"YES"<<endl;
            
            cout<<post[0];
            
            for(int i=1;i<n;i++)cout<<" "<<post[i];
        }
        
        else cout<<"NO";
    }
}

1064 . 完全二叉搜索树 **(自建完全二叉树)

在这里插入图片描述

  • 看到这题一位要像书上那样边填边旋转操作,瞬间蒙了
  • 本题只需要先构造一个完全二叉树即数组表示的完全二叉树,再在对应位置填入中序遍历二叉搜索树的权值即可
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1010;

int w[N],bst[N];              //权值序列,完全二叉树

int n;

//按照中序遍历将w[i]填入bst[]中

void dfs(int & u,int cnt){    //u对应w[i]中对应点(),注意u应当是引用传参,否则坐标不会变化,cnt是当前点应当存入的位置
    
    if(cnt*2<=n)dfs(u,cnt*2);        //如果存在左子树,递归填满左子树
    
    bst[cnt]=w[u++];                 //将对应点填入当前位置
    
    if(cnt*2+1<=n)dfs(u,cnt*2+1);    //如果存在右子树,递归填满右子树
}

int main(){
    
    cin>>n;
    
    for(int i=0;i<n;i++)cin>>w[i];
    
    sort(w,w+n);                       //获得中序遍历
    
    int u=0;
    
    dfs(u,1);
    
    cout<<bst[1];
    
    for(int i=2;i<=n;i++)cout<<" "<<bst[i];
    
}

1086.再次树遍历

在这里插入图片描述

  • 思路一 :push操作建立的是先序遍历,pop操作建立的是中序遍历,根据先序遍历和中序遍历即可得到后序遍历
  • 思路二:上一个操作如果是push,则当前点是上一个点的左二子,如果是pop操作则是右儿子,如果没有上一个操作就是根节点
//思路一

#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>

using namespace std;

const int N=50;

int ino[N],pre[N],post[N],loc[N];

int idx,cnt;

stack<int> s;

void build(int il,int ir,int pl,int pr,int &u){
    
    int root=pre[pl];
    
    int k=loc[root];
    
    int len=k-il;
    
    if(il<k)build(il,k-1,pl+1,pl+len,u);
    
    if(ir>k)build(k+1,ir,pl+len+1,pr,u);
    
    post[u++]=root;
}

int main(){
    
    int n;
    
    cin>>n;
    
    int k=2*n;
    
    while(k--){
        
        string op;
        
        cin>>op;
        
        if(op=="Push"){
            
            int x;
            
            cin>>x;
            
            pre[idx++]=x;
            
            s.push(x);
        }
        
        else{
            
            ino[cnt]=s.top();
            
            loc[ino[cnt]]=cnt;
            
            cnt++;
            
            s.pop();
        }
    }
    
    int u=0;
    
    build(0,n-1,0,n-1,u);
    
    cout<<post[0];
    
    for(int i=1;i<n;i++)cout<<" "<<post[i];
}
//思路二
#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>

using namespace std;

const int N=50;

int l[N],r[N];

stack<int> s;

void dfs(int u,int root){
    
    if(l[u])dfs(l[u],root);
    
    if(r[u])dfs(r[u],root);
    
    cout<<u;
    
    if(u!=root)cout<<" ";           //如果当前输出的根不是根节点,则要加上空格
}

int main(){
    
    int n;
    
    cin>>n;
    
    int last=0,type;   //last表示上一个节点,type为0表示上一个操作是push,type为1表示上一个操作是pop
    
    int root;          //根节点
    
    for(int i=0;i<n*2;i++){
        
        string op;
        
        cin>>op;
        
        if(op=="Push"){
            
            int x;
            
            cin>>x;
            
            if(!last)root =x;
            
            else{
                
                if(!type)l[last]=x;
                    
                else r[last]=x;
            }
            
            s.push(x);
            
            last=x;
            
            type=0;
        }
        
        else {
            
            last=s.top();
            
            s.pop();
            
            type=1;
        }
    }
    
    dfs(root,root);
}

1099.构建二叉搜索树 **(已知树的结构填值)

在这里插入图片描述

  • 告诉已知树的结构,中序遍历填入节点的值,和完全二叉搜索树类似
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=110;

int l[N],r[N];         //存储二叉树左右儿子

int w[N],bst[N];       //存储权值,二叉树对应节点的值

int k;

int q[N];

int n;

void dfs(int u){              //根据中序遍历向二叉树中填入权值
    
    if(l[u]!=-1)dfs(l[u]);
    
    bst[u]=w[k++];
    
    if(r[u]!=-1)dfs(r[u]);
}

void bfs(int u){
    
    int tt=-1,hh=0;
    
    q[++tt]=u;
    
    while(tt>=hh){                   //注意bfs时只需要存储对应点的下标即可
        
        u=q[hh++];
        
        if(l[u]!=-1)q[++tt]=l[u];
        
        if(r[u]!=-1)q[++tt]=r[u];
        
    }
    
    cout<<bst[q[0]];                //最后输出时输出对应点的值
    
    for(int i=1;i<n;i++)cout<<" "<<bst[q[i]];
}

int main(){

    cin>>n;
    
    for(int i=0;i<n;i++)cin>>l[i]>>r[i];
    
    for(int i=0;i<n;i++)cin>>w[i];
    
    sort(w,w+n);
    
    dfs(0);
    
    bfs(0);
}

1102.反转二叉树

在这里插入图片描述


#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=15;

int l[N],r[N];

int n;

bool has_father[N];

void invert(int root){
    
    if(l[root]!=-1)invert(l[root]);
    
    if(r[root]!=-1)invert(r[root]);
    
    swap(l[root],r[root]);
}

int q[N];

void bfs(int root){
    
    int tt=-1,hh=0;
    
    q[++tt]=root;
    
    while(tt>=hh){
        
        int x=q[hh++];
        
        if(l[x]!=-1)q[++tt]=l[x];
        
        if(r[x]!=-1)q[++tt]=r[x];
    }
    
    cout<<q[0];
    
    for(int i=1;i<n;i++)cout<<" "<<q[i];
    
    cout<<endl;
}

int k;

void dfs(int u){
    
    if(l[u]!=-1)dfs(l[u]);
    
    cout<<u;
    
    if(++k!=n)cout<<" ";                  //如果当前点不是最后一个节点,那么输出一个空格
    
    if(r[u]!=-1)dfs(r[u]);
}

int main(){
    
    cin>>n;
    
    memset(l,-1,sizeof l);
    
    memset(r,-1,sizeof r);
    
    for(int i=0;i<n;i++){
        
        char a,b;
        
        cin>>a>>b;
        
        if(a!='-')l[i]=a-'0',has_father[l[i]]=true;           //标记存在父节点,如果不存在父节点,则为根节点
        
        if(b!='-')r[i]=b-'0',has_father[r[i]]=true;
    }
    
    int root;
    
    for(int i=0;i<n;i++)if(!has_father[i])root=i;
    
    invert(root);
    
    bfs(root);
    
    dfs(root);     

}

1110.完全二叉树 ** 判断是否是完全二叉树

在这里插入图片描述

  • 写完就觉得王道书是弟中弟…
  • 思路是根据完全二叉树的存储模式u,2*u,2*u+1....所以如果当前树按顺序存满整个二叉树,该结构内不应该有空值,换句话说,如果最后一个节点的坐标大于n那么它不是一棵完全二叉树
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=30;

int l[N],r[N];

int n;

bool has_father[N];

int last,max_index=-1;             //记录最后一个节点编号,最后一个点对应的下标,

void dfs(int u,int index){
    
    if(u==-1)return ;
    
    dfs(l[u],2*index);
    
    dfs(r[u],2*index+1);
    
    if(index>max_index){          //如果当前节点下标比较大,更新最后一个点的下标和对应的编号
        
        max_index=index;
        
        last=u;
    }
}


int main(){
    
    cin>>n;
    
    memset(l,-1,sizeof l);
    
    memset(r,-1,sizeof r);
    
    for(int i=0;i<n;i++){
        
        string a,b;
        
        cin>>a>>b;
        
        if(a!="-")l[i]=stoi(a),has_father[l[i]]=true;
        
        if(b!="-")r[i]=stoi(b),has_father[r[i]]=true;
    }
    
    int root;
    
    for(int i=0;i<n;i++)if(!has_father[i])root=i;
    
    dfs(root,1);
    
    if(max_index<=n){                   //如果最后一个点的下标合法,那么是完全二叉树
        
        cout<<"YES"<<" "<<last;
    }
    
    else cout<<"NO"<<" "<<root;
}

1115.二叉搜索树最后两层点的数量 ** (自定义节点序号建树) **

在这里插入图片描述

  • 这是写到现在写的最nb的一个递归代码了
  • 首先题目只给了权值而没有给编号,所以我们用idx=1,2,3,4,...为每次插入的值编号,作为其在树中的编号
  • 其次我们规定当一个节点的root值为0,代表这个节点为空,还没有建立节点,这时我们新分配一个idx值,创建一个新节点并且赋予权值
  • 我们每次保证从根节点开始递归的向下插入节点,如果遇到当前root不为0,那么比较一下权值,再插入这个节点的L[root]或者R[root]
  • 一共需要插入n个节点,每次插入需要比较logn次,所以时间复杂度近似为nlogn
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1010;

int n;

int idx;                         //自定节点序号,idx从1开始分配

int l[N],r[N],v[N];              //节点左右子节点,权值

void insert(int & root,int w){   //注意传值为引用,这样才能让l[root]和r[root]正确赋值
    
    if(!root){                   //root为0,代表当前节点不存在,建立一个新的节点,并且赋予权值和节点序号idx;
        
        v[++idx]=w;         
        
        root=idx;
    }
    
    else if(w<=v[root])insert(l[root],w);    //新插入的节点与当前节点权值比较,若<=则插入左子树,否则插入右子树,同时记录当前"根"的左右儿子坐标
    
    else insert(r[root],w);

}

int cnt[N],max_depth;                 //记录一下每一层的节点数量以及最大深度

void dfs(int root,int depth){
    
    if(!root)return;
    
    cnt[depth]++;
    
    if(depth>max_depth)max_depth=depth;
    
    dfs(l[root],depth+1);
    
    dfs(r[root],depth+1);
}

int main(){
    
    cin>>n;
    
    int root=0;
    
    for(int i=0;i<n;i++){
        
        int w;
        
        cin>>w;
        
        insert(root,w);    //注意root值最终是1保持不变,这样使得每一次都能从根节点向下递归建树
    }
    
    dfs(root,0);
    
    int sum=cnt[max_depth]+cnt[max_depth-1];
    
    printf("%d + %d = %d",cnt[max_depth],cnt[max_depth-1],sum);
}

1119.前序和中序遍历 ** (先序和后序建树)

在这里插入图片描述

  • 这道题由于先序遍历和后序遍历可能确定不了唯一的树,所以我们在dfs时考虑所有可能的情况
  • 由于先序遍历=根节点(>=0)+左子树序列(>=0)+右子树序列(>=0),后序遍历=左子树序列(>=0)+右子树序列(>=0)+根节点(>=0),所以我们枚举左右子树所有可能的长度,cnt累计所有可能的结果,当cnt大于1时
    说明该树不唯一,此时我们停止枚举并且返回结果

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N= 50;

int pre[N],post[N];

int n;

int dfs(int l1,int r1,int l2,int r2,string& in){        //pre 左右节点l1,r1,  post左右节点l2,r2;
    
    if(l1>r1)return 1;                                  //如果为空树,合法,返回1
    
    if(pre[l1]!=post[r2])return 0;                      //如果前序头结点和后序尾结点值不同,则不合法,返回0
    
    int cnt=0;
    
    int len=r1-l1;
    
    for(int i=0;i<=len;i++){                            //枚举左子树长度(0~n-1),右子树长度为(n-1~0)
        
        string lin,rin;
        
        int lcnt=dfs(l1+1,l1+i,l2,l2+i-1,lin);         //返回左子树,右子树的所有可能
        
        int rcnt=dfs(l1+i+1,r1,l2+i,r2-1,rin);
        
        if(lcnt && rcnt){                              //如果左右子树可能性都大于1,并且cnt总数大于1,则情况不唯一,不用继续暴搜,直接返回结果
            
            in=lin+to_string(pre[l1])+" "+rin;         //左右子树都合法则更新中序遍历结果,(to_string 可能自带空格???)
            
            cnt+=lcnt*rcnt;
            
            if(cnt>1)break;
        }
    }
    
    return cnt;
}

int main(){
    
    cin>>n;
    
    for(int i=0;i<n;i++)cin>>pre[i];
    
    for(int i=0;i<n;i++)cin>>post[i];
    
    string res;
    
    int cnt=dfs(0,n-1,0,n-1,res);
    
    if(cnt>1)cout<<"No"<<endl;
    
    else cout<<"Yes"<<endl;
    
    res.pop_back();
    
    cout<<res<<endl;
    
    return 0;
}

1620.Z字形遍历二叉树

在这里插入图片描述

  • 这题思路比较简单,只要构建出二叉树然后再bfs时标记一下奇数层节点,并且每遍历完奇数层节点就这一层进行翻转即可
#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>

using namespace std;

const int N=50;

int ino[N],post[N];

unordered_map<int,int> l,r,loc;             //注意这题节点值并不是按照1,2,3,....顺序递增的,所以用unordered_map存储避免空间不够

int n;

int dfs(int il,int ir,int pl,int pr){
    
    int root=post[pr];
    
    int k=loc[root];
    
    if(il<k)l[root]=dfs(il,k-1,pl,pl+k-il-1);
    
    if(ir>k)r[root]=dfs(k+1,ir,pl+k-il,pr-1);
    
    return root; 
}

int q[N];

void bfs(int root,int u){
    
    int hh=0,tt=-1;
    
    q[++tt]=root;
    
    int len=1;
    
    int pre_hh=0,pre_tt=0;              //记录一下上一层的节点坐标
    
    while(hh<=tt){
        
        int x=q[hh++];
        
        len--;
        
        if(l[x])q[++tt]=l[x];
        
        if(r[x])q[++tt]=r[x];
        
        if(!len){                                   //这一层节点全部入队之后,考虑要不要翻转上一层节点
            
            len=tt-hh+1;
            
            if(u%2)reverse(q+pre_hh,q+pre_tt+1);
            
            u++;
            
            pre_hh=hh,pre_tt=tt;
        }
    }
    
    cout<<q[0];
    
    for(int i=1;i<n;i++)cout<<" "<<q[i];
}


int main(){
    
    cin>>n;
    
    for(int i=0;i<n;i++){
        
        cin>>ino[i];
        
        loc[ino[i]]=i;
    }
    
    for(int i=0;i<n;i++)cin>>post[i];

    int root=dfs(0,n-1,0,n-1);
    
    bfs(root,1);
    
    return 0;
}


//y总的写法更为简单一些,思路一致
#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>

using namespace std;

const int N=50;

int ino[N],post[N];

unordered_map<int,int> l,r,loc;

int n;

int dfs(int il,int ir,int pl,int pr){
    
    int root=post[pr];
    
    int k=loc[root];
    
    if(il<k)l[root]=dfs(il,k-1,pl,pl+k-il-1);
    
    if(ir>k)r[root]=dfs(k+1,ir,pl+k-il,pr-1);
    
    return root; 
}

int q[N];

void bfs(int root){
    
    int hh=0,tt=-1;
    
    q[++tt]=root;
    
    int cnt=0;
    
    while(hh<=tt){
        
        int head=hh,tail=tt;
        
        while(hh<=tail){
            
            int x=q[hh++];
            
            if(l[x])q[++tt]=l[x];
            
            if(r[x])q[++tt]=r[x];
            
        }
            
        if(++cnt%2)reverse(q+head,q+tail+1);
        
    }
    
    cout<<q[0];
    
    for(int i=1;i<n;i++)cout<<" "<<q[i];
}


int main(){
    
    cin>>n;
    
    for(int i=0;i<n;i++){
        
        cin>>ino[i];
        
        loc[ino[i]]=i;
    }
    
    for(int i=0;i<n;i++)cin>>post[i];

    int root=dfs(0,n-1,0,n-1);
    
    bfs(root);
    
    return 0;
}


1138.后序遍历

在这里插入图片描述

  • 用哈希做优化标记中序遍历值的位置,递归输出结果即可
#include<iostream>
#include<cstring>
#include<unordered_map>

using namespace std;

const int N=50010;

unordered_map<int,int> l,r,loc;             //loc要用unordered_map做优化,int数组不行

int pre[N],ino[N];

int n;

int res;

int dfs(int pl,int pr,int il,int ir){
    
    int root=pre[pl];
    
    int k=loc[root];
    
    if(il<k)return dfs(pl+1,pl+k-il,il,k-1);
    
    else if(ir>k) return dfs(pl+k-il+1,pr,k+1,ir);
    
    else return root;
}

int main(){
    
    cin>>n;
    
    for(int i=0;i<n;i++)cin>>pre[i];
    
    for(int i=0;i<n;i++){
        
        cin>>ino[i];
        
        loc[ino[i]]=i;
    }
    
    cout<<dfs(0,n-1,0,n-1);
}

1066.AVL(二叉平衡搜索树)的根 ** (平衡树的旋转) **

99o9

  • 本题模拟平衡树的旋转调整操作,用的是之前自定义节点编号的插入方式(NlogN),注意写法!!
#include<algorithm>
#include<iostream>

using namespace std;

const int N=50;

int l[N],r[N],v[N],h[N],idx;           //左儿子,右儿子,节点权值,节点高度,节点编号

int n;

int update(int u){
    
    h[u]=max(h[l[u]],h[r[u]])+1;    //用于更新插入或者变更节点后更新树高
}

int get_balance(int u){
    
    return h[l[u]]-h[r[u]];           //获得当前节点的平衡值
}

void R(int &u){                      //右旋操作
    
    int p=l[u];
    
    l[u]=r[p],r[p]=u;
    
    update(u),update(p);
    
    u=p;
}

void L(int &u){                      //左旋操作
    
    int p=r[u];
    
    r[u]=l[p],l[p]=u;
    
    update(u),update(p);
    
    u=p;
}

void insert(int & u,int w){
    
    if(!u){                            //建立新的节点
        
        u=++idx;
        
        v[u]=w;
    }
    
    else if(w<v[u]){                    // 否则插入左节点
        
        insert(l[u],w);
        
        if(get_balance(u)==2){                  //说明插入到左子树高度超过平衡树限制
            
            if(get_balance(l[u])==1)R(u);      //说明插入了左子树的左节点,右旋当前节点
            
            else L(l[u]),R(u);                 //否则插入了左子树的右节点,先左旋左子树,再右旋根节点    
        }
    }
    
    else {
        
        insert(r[u],w);               // 否则插入右节点
        
        if(get_balance(u)==-2){
            
            if(get_balance(r[u])==-1)L(u);
            
            else R(r[u]),L(u);
        }
    }
    
    update(u);                        //更新节点高度
}

int main(){
    
    cin>>n;
    
    int root=0;
    
    while(n--){
        
        int w;
        
        cin>>w;
        
        insert(root,w);
    }
    
    cout<<v[root];
}


1123.判断完全AVL树

在这里插入图片描述

  • 这道题是完全二叉树和AVL树的综合,首先根据题目构造出AVL树,然后利用完全二叉树的节点序号特质判断其是否是一个完全AVL树
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=30;

int l[N],r[N],h[N],v[N],idx;

int n;

void update(int u){
    
    h[u]=max(h[l[u]],h[r[u]])+1;
}

int get_balance(int u){
    
   return h[l[u]]-h[r[u]]; 
}

void R(int &u){
      
    int p=l[u];
    
    l[u]=r[p],r[p]=u;
    
    update(u),update(p);
    
    u=p;
}

void L(int &u){
    
    int p=r[u];
    
    r[u]=l[p],l[p]=u;
    
    update(u),update(p);
    
    u=p;
}

void insert(int & u,int w){
    
    if(!u){
        
        u=++idx;
        
        v[u]=w;
    }
    
    else if(w<v[u]){
        
        insert(l[u],w);
        
        if(get_balance(u)==2){
            
            if(get_balance(l[u])==1)R(u);
            
            else L(l[u]),R(u);
        }
    }
    
    else{
        
        insert(r[u],w);
        
        if(get_balance(u)==-2){
            
            if(get_balance(r[u])==-1)L(u);
            
            else R(r[u]),L(u);
        }
    }
    
    update(u);
}

int q[N],pos[N];

bool res=true;

void bfs(int root){
    
    int hh=0,tt=-1;
    
    q[++tt]=root;
    
    pos[root]=1;
    
    while(hh<=tt){
        
        int x=q[hh++];
        
        if(pos[x]>n)res=false;                               //如果某个节点的序号大于n,那么他不是一棵完全二叉排序树
        
        if(l[x])q[++tt]=l[x],pos[l[x]]=pos[x]*2;      //一边BFS这颗树,一边存储其节点对应的完全二叉树中的下标
        
        if(r[x])q[++tt]=r[x],pos[r[x]]=pos[x]*2+1;
    }
    
    cout<<v[q[0]];
    
    for(int i=1;i<idx;i++)cout<<" "<<v[q[i]];
    
    cout<<endl;
}

int main(){
    
    cin>>n;
    
    int root=0;
    
    for(int i=0;i<n;i++){
        
        int w;
        
        cin>>w;
        
        insert(root,w);
    }
    
    bfs(root);
    
    if(res)cout<<"YES"<<endl;
    
    else cout<<"NO"<<endl;
}

1135.判断红黑树 **

在这里插入图片描述

  • 思路:边递归建树,边判断是否是红黑树,该树满足如下条件
  • 1.根节点是黑色
  • 2.红色节点的左右儿子都是黑色(注意空节点也算黑色,按照0)
  • 3.到任意叶节点上的黑色节点一定数目相同
  • 4.前序遍历不一定可以构建一棵二叉搜索树
  • PS: 注意这题节点中序遍历是不带负值的,只是存的时候加上负数标记为红色节点

#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>

using namespace std;

const int N=50;

int pre[N],ino[N];                             //先序遍历存带权节点,中序遍历存绝对值

unordered_map<int,int> pos;                    //对应中序遍历节点坐标

bool res;

int build(int il,int ir,int pl,int pr,int &sum){     //建树,sum记录从当前节点到达叶节点的黑色节点数量
    
    int root=pre[pl];
    
    int k=pos[abs(root)];
    
    if(k<il || k>ir){                                 //中序遍历中没有当前root对应的序号,返回false
        
        res=false;
        
        return -1;
    }
    
    int lt=0,rt=0,lsum=0,rsum=0;                     //lt,rt记录左右根节点的值(若不存在则默认为0(黑色节点)),以及左右节点的路径黑色节点
    
    if(il<k)lt=build(il,k-1,pl+1,pl+k-il,lsum);
    
    if(k<ir)rt=build(k+1,ir,pl+k-il+1,pr,rsum);
    
    if(lsum!=rsum){
        
        res=false;                       //左右路径黑色节点数量不同,返回false
        
        return -1;
    }
    
    sum=lsum;
    
    if(root<0){                                    //如果当前节点为红色,并且左右节点有一个红色,则返回false
        
        if(lt<0||rt<0)res=false;
    
        return -1;
    }
    
    else  sum++;                                  //否则当前节点为黑色节点,路径黑色节点数量+1
    
    return root;
}

int main(){
    
    int k;
    
    cin>>k;
    
    while(k--){
        
        int n;
        
        cin>>n;
        
        for(int i=0;i<n;i++){
            
            cin>>pre[i];                    //pre记录带有负值的先序遍历
        
            ino[i]=abs(pre[i]);            //中序遍历有序,取绝对值排序
        }
        
        sort(ino,ino+n);
        
        pos.clear();
        
        for(int i=0;i<n;i++)pos[ino[i]]=i;
        
        int sum=0;
        
        res=true;
        
        int root=build(0,n-1,0,n-1,sum);
        
        if(root<0)res=false;              //根节点为红色,非法
        
        if(res)cout<<"Yes"<<endl;
        
        else cout<<"No"<<endl;
    }
}

1053.等重路径

在这里插入图片描述

  • 这题直接用邻接表保存树的路径节点,再暴搜出每一条等重路径即可
  • Ps:题目结果要求总大到小输出字典序,我们直接利用vector特性geater<int>()输出大端排序结果即可


#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;

const int N=110;

int h[N],e[N],ne[N],w[N],idx;     //表头,节点下标,邻接节点,权值序列,地址空间

int n,m,s;

void insert(int a,int b){            //邻接表插入操作
    
    e[idx]=b;
    
    ne[idx]=h[a];
    
    h[a]=idx++;
    
}

vector<vector<int> > res;       //二维vector存储结果

void dfs(int root,int sum,vector<int> &path){     //当前节点下标,当前路径权值总和,当前预存路径
    
    if(h[root]==-1 && sum==s)res.push_back(path);  //如果搜到叶子节点并且满足路径权值和等于给定值,加入答案
    
    for(int i=h[root];i!=-1;i=ne[i]){     //dfs所有邻接点
        
        int x=e[i];
        
        path.push_back(w[x]);
        
        dfs(x,sum+w[x],path);
        
        path.pop_back();               //恢复现场
    }
}

int main(){
    
    cin>>n>>m>>s;
    
    for(int i=0;i<n;i++)cin>>w[i];
    
    memset(h,-1,sizeof h);
    
    for(int i=0;i<m;i++){
        
        int id, k;
        
        cin>>id>>k;
        
        while(k--){
            
            int x;
            
            cin>>x;
            
            insert(id,x);
        }

    }
    
    vector<int> path({w[0]});
    
    dfs(0,w[0],path);
    
    sort(res.begin(),res.end(),greater<vector<int>>());      //按照字典序从大到小输出,注意greater写法
    
    for(auto it :res){
        
        cout<<it[0];
        
        for(int i=1;i<it.size();i++)cout<<" "<<it[i];
        
        cout<<endl;
    }
}

1094.最大的一代

在这里插入图片描述

  • 这道题就是用dfs或者bfs搜索每一层的节点数量,最后输出最大值
  • 提供两种写法邻接表存储的dfs邻接矩阵存储的bfs
//dfs

#include<iostream>
#include<algprithm>
#include<cstring>

using namespace std;

const int N=110;

int h[N],e[n],ne[n],idx;

int n,m;

void insert(int a,int b){      //邻接表
    
    e[idx]=b;
    
    ne[idx]=h[a];
    
    h[a]=idx++;
}

unordered_map<int,int> has;

bool flag[N];                 //标记是否遍历过当前节点

void dfs(int root,int u){
    
    if(!flag[root]){
        
        has[u]++;
        
        flag[root]=true;
    }
    
    for(int i=h[root];i!=-1;i=ne[i]){
        
        int x=e[i];
        
        dfs(x,u+1);
    }
}

int main(){
    
    cin>>n>>m;
    
    memset(h,-1,sizeof h);
    
    while(m--){
        
        int id,k;
        
        while(k--){
            
            int b;
            
            cin>>b;
            
            insert(id,b);
        }
    }
    
    int u=1;
    
    dfs(1,u);
    
    int max_u=1,max_sum=1;
    
    for(int i=0;i<has.size();i++){
        
        int u=has.first,sum=has.second;
        
        if(sum>max_sum){
            
            max_sum=sum;
            
            max_u=u;
        }
    }
    
    cout<<max_sum<<" "<<max_u;
}


//bfs
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>

using namespace std;

const int N=110;

bool map[N][N];      //存储邻接矩阵

vector<int> level[N];   //存储每层节点数量

int n,m;

int main(){
    
    cin>>n>>m;
    
    while(m--){
        
        int id,k;
        
        cin>>id>>k;
        
        while(k--){
            
            int son;
            
            cin>>son;
            
            map[id][son]=true;    //id到son存在一条有向边
        }
    }
    
    int l=1;
    
    level[l].push_back(1);    //第一层有根节点一个点
    
    while(level[l].size()){
        
        for(auto it:level[l]){
            
            for(int j=1;j<=n;j++){
                
                if(map[it][j])level[l+1].push_back(j);
            }
        }
        
        l++;
    }
    
    int k=1;
    
    for(int i=2;i<l;i++)
        
        if(level[i].size()>level[k].size())k=i;
    
    cout<<level[k].size()<<" "<<k<<endl;
    
}

1079.供应链总销售额 (记忆化搜索/树形DP)

在这里插入图片描述

  • 树形DP根据该树特点递推出每个叶子节点到达根节点的距离,即计算f[N]
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1e5+10;

int h[N],e[N],ne[N],idx;

int n;

double p,r,sum;

int cnt[N];

void insert(int a,int b){
    
    e[idx]=b;
    
    ne[idx]=h[a];
    
    h[a]=idx++;
}

void dfs(int root,double value){
    
    if(h[root]==-1){
        
        sum+=cnt[root]*value;
        
        return;
    }
    
    for(int i=h[root];i!=-1;i=ne[i]){
        
        int j=e[i];
        
        dfs(j,value*(1+r/100));
    }
}

int main(){
    
    cin>>n>>p>>r;
    
    memset(h,-1,sizeof h);
    
    for(int i=0;i<n;i++){
        
        int k,x;
        
        cin>>k;
        
        if(!k){
            
            cin>>x;
            
            cnt[i]=x;
        }
        
        while(k--){
            
            cin>>x;
            
            insert(i,x);
        }
    }
    
    dfs(0,p);
    
    printf("%.1lf",sum);
    
}


//树形DP

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

const int N=1e5+10;

int n;

double P,R;

int p[N],f[N],cnt[N];               //记录每个节点的父节点,到达根节点的路径大小,叶节点的售卖数量

int dfs(int root){                  //记忆化搜索
    
    if(f[root]!=-1)return f[root];                  //如果当前路径已经更新过,直接返回其值
    
    else if(p[root]!=-1)return f[root]=dfs(p[root])+1;      //如果当前点不是根节点就返回父节点的路径长度+1
    
    else return f[root]=0;                          //如果是根节点就为返回0,并且为根节点f赋值
}

int main(){
    
    cin>>n>>P>>R;
    
    memset(p,-1,sizeof p);                 //初始每个节点父节点均为-1(表示没有)
    
    memset(f,-1,sizeof f);                //路径初始化为-1,因为根节点路径长度为0
    
    for(int i=0;i<n;i++){
        
        int k;
        
        cin>>k;
        
        if(!k){
            
            int num;
            
            cin>>num;
            
            cnt[i]=num;
        }
        
        while(k--){
            
            int son;
            
            cin>>son;
            
            p[son]=i;
        }
    }
    
    double sum=0;
    
    for(int i=0;i<n;i++){
        
        if(cnt[i])sum+=P*cnt[i]*pow((1+R/100),dfs(i));           //对于每个叶节点,计算价格即P * cnt * (1+r/100)^(f路径长度次方) 
    }
    
    printf("%.1lf",sum);
    
}

1090.供应链的最高价格

在这里插入图片描述

  • 这两题都和总销售额做法一模一样,不做赘述
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=1e5+10;

int h[N],e[N],ne[N],idx;

int n;

double P,R;

void insert(int a,int b){
    
    e[idx]=b;
    
    ne[idx]=h[a];
    
    h[a]=idx++;
}

double max_value;

int cnt;

void dfs(int root,double value){
    
    if(h[root]==-1){
        
        if(value>max_value){
            
            max_value=value;
            
            cnt=1;
            
        }
        
        else if(value==max_value)cnt++;
        
        return;
    }
    
    for(int i=h[root];i!=-1;i=ne[i]){
        
        int j=e[i];
        
        dfs(j,(1+R/100)*value);
    }
}

int main(){
    
    cin>>n>>P>>R;
    
    memset(h,-1,sizeof h);
    
    int root=0;
    
    for(int i=0;i<n;i++){
        
        int father;
        
        cin>>father;
        
        if(father==-1)root=i;
        
        else insert(father,i);
    }
    
    dfs(root,P);
    
    printf("%.2lf %d",max_value,cnt);
    
}



#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;

const int N=1e5+10;

int f[N],p[N];

int n;

double P,R;

int dfs(int u){
    
    if(f[u]!=-1)return f[u];
    
    else if(p[u]==-1)return f[u]=0;
    
    return f[u]=dfs(p[u])+1;
}

int main(){
    
    cin>>n>>P>>R;
    
    memset(f,-1,sizeof f);
    
    memset(p,-1,sizeof p);
    
    for(int i=0;i<n;i++)cin>>p[i];
    
    int cnt=0,path=0;
    
    double value=0;
    
    for(int i=0;i<n;i++){
        
        int len=dfs(i);
        
        if(len>path){
            
            value=P*pow(1+R/100,len);
            
            path=len;
            
            cnt=1;
        }
        
        else if(len==path)cnt++;
    }
    
    printf("%.2lf %d",value,cnt);
}

1106.供应链最低价格

在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=1e5+10;

int h[N],e[N],ne[N],idx;

int n;

double P,R;

void insert(int a,int b){
    
    e[idx]=b;
    
    ne[idx]=h[a];
    
    h[a]=idx++;
}

double min_value=1e10;

int cnt;

void dfs(int root,double value){
    
    if(h[root]==-1){
        
        if(value<min_value){
            
            min_value=value;
            
            cnt=1;
            
        }
        
        else if(value==min_value)cnt++;
        
        return;
    }
    
    for(int i=h[root];i!=-1;i=ne[i]){
        
        int j=e[i];
        
        dfs(j,(1+R/100)*value);
    }
}

int main(){
    
    cin>>n>>P>>R;
    
    memset(h,-1,sizeof h);
    
    for(int i=0;i<n;i++){
        
        int k;
        
        cin>>k;
        
        if(!k)continue;
        
        while(k--){
            
            int son;
            
            cin>>son;
            
            insert(i,son);
        }
    }
    
    dfs(0,P);
    
    printf("%.4lf %d",min_value,cnt);
    
}

1155.堆路径

在这里插入图片描述

  • 用数组存完全二叉树,按照根右左的顺序遍历二叉树,保存每一条路径,最后判断所有路径是否统一递增或者递减

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;

const int N=1010;

int w[N];                                     //存完全二叉树

int n;

vector<vector<int> > res;                    //保存最终结果

vector<int> path;                            //保存当前路径

void dfs(int u,vector<int> & path){
    
    path.push_back(w[u]);
    
    if(2*u>n)res.push_back(path);           //如果是叶节点,保存路径
    
    if(2*u+1<=n)dfs(2*u+1,path);            //否则遍历右左节点
    
    if(2*u<=n)dfs(2*u,path);
    
    path.pop_back();                        //恢复现场
}

int main(){
    
    cin>>n;
    
    for(int i=1;i<=n;i++)cin>>w[i];
    
    dfs(1,path);
    
    bool gt=false,lt=false;
    
    for(auto it:res){
        
        cout<<it[0];
        
        for(int i=1;i<it.size();i++){
            
            if(it[i]>it[i-1])gt=true;        //存在递增序列
            
            if(it[i]<it[i-1])lt=true;        //存在递减序列
            
            cout<<" "<<it[i];
        }
        
        cout<<endl;
    }
    
    if(gt && lt)cout<<"Not Heap"<<endl;
    
    else if(gt)cout<<"Min Heap"<<endl;
    
    else cout<<"Max Heap"<<endl;
}

1130.中缀表达式

在这里插入图片描述

  • 这道题通过中根遍历输出表达式,要点在于添加括号
  • 首先整体不用添加括号,其次左右子节点如果是单独的叶节点通用不用添加括号,递归输出即可
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=25;

int l[N],r[N];

string w[N];

bool st[N],is_leaf[N];

int n;

string dfs(int u){
    
    string left,right;
    
    if(l[u]!=-1){
        
        if(!is_leaf[l[u]])left="("+dfs(l[u])+")";     //如果左节点不为空并且不为叶节点则对整个左子树添加左右括号
        
        else left=dfs(l[u]);                            //否则不添加括号
    }
    
    if(r[u]!=-1){
        
        if(!is_leaf[r[u]])right="("+dfs(r[u])+")";
        
        else right=dfs(r[u]);
    }
    
    
    return left+w[u]+right;
}

int main(){
    
    cin>>n;
    
    int root=1;
    
    for(int i=1;i<=n;i++){
        
        cin>>w[i]>>l[i]>>r[i];
        
        if(l[i])st[l[i]]=true;            //标记存在父节点的节点以找到根节点
        
        if(r[i])st[r[i]]=true;
        
        if(l[i]==-1 && r[i]==-1)is_leaf[i]=true;   //标记叶子结点
        
    }
    
    while(st[root])root++;
    
    cout<<dfs(root);
}

1143.最低公共祖先 **(LCA)

在这里插入图片描述

  • 本题是寻找最低公共祖先(LCA)的简单实现方法
  • 首先按照中序和先序遍历重建二叉树(可以不重建),重建过程中记录每个节点的深度以及父节点
  • 寻找过程就是从给定两节点出发,依次向上寻找直到两节点重合即可,时间复杂度O(N)
  • 由于本题时间限制2*10^7,而数据范围是10^7,所以直接使用哈希表寻找中序遍历位置时可能会超时
  • 所以我们将数值映射到0~N-1的下标位置,并用`下标表示中序遍历和先序遍历
  • 例如: 先序遍历1,5,7,4,3,2 对应中序遍历->1,2,3,4,5,7 中序遍历对应下标-> 0,1,2,3,4,5 ,则先序遍历对应下标0,4,5,3,2,1 即在计算谁是谁的父节点时,只需要知道下标,不需要知道具体数值
#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>

using namespace std;

const int N=10010;

int in[N],pre[N],seq[N];           //in[N],pre[n]存离散化后的中序遍历和先序遍历,seq存input数据

int p[N],depth[N];        //记录每个节点的父节点及其深度

int n,m;

unordered_map<int,int> pos;        //帮助映射(离散化)

int build(int il,int ir,int pl,int pr,int d){
    
    int root=pre[pl];
    
    int k=root;               //中序遍历点的位置
    
    depth[root]=d;
    
    if(il<k)p[build(il,k-1,pl+1,pl+1+(k-1-il),d+1)]=root;   //建树时只初始化每个节点的父节点
    
    if(ir>k)p[build(k+1,ir,pl+k-il+1,pr,d+1)]=root;
    
    return root;
}

int main(){
    
    cin>>m>>n;
    
    for(int i=0;i<n;i++){
        
        cin>>pre[i];
        
        seq[i]=pre[i];
    }
    
    sort(seq,seq+n);                     //找出二叉排序树的中序遍历
    
    for(int i=0;i<n;i++){
        
        pos[seq[i]]=i;                    //将中序遍历每个数值映射到0~N-1  
        
        in[i]=i;                           //中序遍历就是从小到大的0~n-1
    }
    
    for(int i=0;i<n;i++)pre[i]=pos[pre[i]];       //重新离散化先序遍历
    
    build(0,n-1,0,n-1,0);
    
    while(m--){
        
        int a,b;
        
        cin>>a>>b;
        
        if(pos.count(a) && pos.count(b)){
            
            a=pos[a],b=pos[b];
            
            int x=a,y=b;
            
            while(a!=b){
                
                if(depth[a]<depth[b])b=p[b];
                
                else a=p[a];
            }
            
            if(a!=x && a!=y)printf("LCA of %d and %d is %d.\n",seq[x],seq[y],seq[a]);
            
            else if(a==x)printf("%d is an ancestor of %d.\n",seq[x],seq[y]);
            
            else printf("%d is an ancestor of %d.\n",seq[y],seq[x]);
        }
        
        else if(pos.count(a)==0 && pos.count(b)==0)printf("ERROR: %d and %d are not found.\n",a,b);
        
        else if(pos.count(a)==0)printf("ERROR: %d is not found.\n",a);
        
        else printf("ERROR: %d is not found.\n",b);
    }
}

1151.二叉树中的最低公共祖先

在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<unordered_map>

using namespace std;

const int N=10010;

int p[N],depth[N];

int ino[N],pre[N],seq[N];   //ino,pre只用保存中序和先序下标,seq保存原中序序列

unordered_map<int,int> pos;   //用于离散化

int build (int pl,int pr,int il,int ir,int d){
    
    int root = pre[pl];
    
    int k=root;
    
    depth[root]=d;
    
    if(il<k)p[build(pl+1,pl+1+k-1-il,il,k-1,d+1)]=root;
    
    if(ir>k)p[build(pl+1+k-il,pr,k+1,ir,d+1)]=root;
    
    return root;
}

int main(){
    
    int m,n;
    
    cin>>m>>n;
    
    for(int i=0;i<n;i++){
        
        cin>>seq[i];
        
        ino[i]=seq[i];
        
        pos[ino[i]]=i;
        
        ino[i]=i;
    }
    
    for(int i=0;i<n;i++){
        
        cin>>pre[i];
        
        pre[i]=pos[pre[i]];
    }
    
    build(0,n-1,0,n-1,0);
    
    while(m--){
        
        int a, b;
        
        cin>>a>>b;
        
        if(pos.count(a)&& pos.count(b)){
            
            a=pos[a],b=pos[b];
            
            int x=a,y=b;
            
            while(a!=b){
                
                if(depth[a]<depth[b])b=p[b];
                
                else a=p[a];
            }
            
            if(a!=x && a!=y)printf("LCA of %d and %d is %d.\n",seq[x],seq[y],seq[a]);
            
            else if(a==x)printf("%d is an ancestor of %d.\n",seq[x],seq[y]);
            
            else printf("%d is an ancestor of %d.\n",seq[y],seq[x]);
        }
        
        else if(pos.count(a)==0 && pos.count(b)==0)printf("ERROR: %d and %d are not found.\n",a,b);
        
        else if(pos.count(a)==0)printf("ERROR: %d is not found.\n",a);
        
        else printf("ERROR: %d is not found.\n",b);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值