PAT_甲級_1151 LCA in a Binary Tree

題目大意:

給定一顆含有N個節點的前序和中序序列,要求給定任意2個節點,須要輸出其最近公共祖先。node

算法思路:

這裏和1143同樣給出2種思路,一種不用建樹,一種須要建樹。ios

算法思路1(不用建樹):

咱們借鑑建樹的遞歸過程完成樹的部分搜索,若是當前搜索的子樹的根節點和查詢節點U和V相等,說明其中之一就是祖先,直接保存並返回便可,不然獲取U和V是否在左子樹或者右子樹的標誌,若是都在左子樹,那麼就都往左子樹搜索,若是都在右子樹,那麼就都往右子樹搜索,不然說明當前子樹的根節點就是U和V的最近公共祖先,直接保存並返回便可,具體作法就是,在輸入中序序列的時候,使用pos保存每個節點在中序遍歷序列中的位置,那麼每一次遞歸查找中序序列根節點的位置就能夠替換爲int k = pos[pre[preL]];而後再使用uInRight和vInRight分別表示U和V是否在右子樹,true表示在右子樹,獲取的方法也很簡單,就是pos[U]>kpos[V]>k便可,而後再根據uInRight和vInRight的值,要麼遞歸搜索,要麼保存祖先ancestor = pre[preL];並返回便可。算法

算法思路2(建樹):

首先根據前序和中序創建一顆二叉樹,而後利用層序遍歷的方法得到每個節點的父親和其所在層數,對於輸入的節點x和y,若是節點所在層數同樣,那麼只要二者不相等就一同向上搜索,直到相遇,其相遇節點即爲最近公共祖先,不然讓層數更大的那個先向上走,直到和另一個節點層數相同,而後再同時向上,直到相遇爲止。數組

注意點:

  • 一、對於算法思路一出現的測試點4超時現象,須要使用pos記錄每個節點在中序序列的位置,這是關鍵。

提交結果:

image.png

AC代碼1(推薦):

#include<cstdio>

using namespace std;

const int maxn = 0x3ffffff;
int N,M;// 節點數目和測試數目
int pre[maxn],in[maxn];
bool isValid[maxn];
int pos[maxn];// 每個節點在中序序列中的下標,不會超時的關鍵

void createTree(int preL,int preR,int inL,int inR,int &ancestor,int U,int V){
    if(preL>preR) return;
    if(pre[preL]==U||pre[preL]==V){
        ancestor = pre[preL]==U?U:V;
        return ;
    }
    // 找到根節點在中序序列中的位置
    int k = pos[pre[preL]];// 直接獲取位置
    int numOfLeft = k-inL;// 左子樹節點個數
    bool uInRight = pos[U]>k,vInRight = pos[V]>k;// U和V在左子樹仍是右子樹,true在右子樹
    if(uInRight&&vInRight){
        // U和V都在右子樹
        createTree(preL+numOfLeft+1,preR,k+1,inR,ancestor,U,V);
    } else if(!uInRight&&!vInRight){
        // U和V都在左子樹
        createTree(preL+1,preL+numOfLeft,inL,k-1,ancestor,U,V);
    } else {
        // U和V分別在左右子樹
        ancestor = pre[preL];
        return;
    }
}

int main(){
    scanf("%d %d",&M,&N);
    for (int i = 0; i < N; ++i) {
        scanf("%d",&in[i]);
        isValid[in[i]] = true;
        pos[in[i]] = i;
    }
    for (int i = 0; i < N; ++i) {
        scanf("%d",&pre[i]);
    }
    for (int j = 0; j < M; ++j) {
        int u,v;
        scanf("%d %d",&u,&v);
        if(!isValid[u]&&!isValid[v]){
            printf("ERROR: %d and %d are not found.\n",u,v);
        } else if(!isValid[u]){
            printf("ERROR: %d is not found.\n",u);
        } else if(!isValid[v]){
            printf("ERROR: %d is not found.\n",v);
        } else {
            int ancestor = -1;
            createTree(0,N-1,0,N-1,ancestor,u,v);
            if (ancestor==u||ancestor==v){
                printf("%d is an ancestor of %d.\n",ancestor,ancestor==u?v:u);
            } else {
                printf("LCA of %d and %d is %d.\n",u,v,ancestor);
            }
        }
    }
    return 0;
}

AC代碼2:

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<queue>
#include<algorithm>

using namespace std;

struct node{
    int data;
    int level;
    node* lchild;
    node* rchild;
    node* parent; 
}; 

const int maxn = 200005;
int pre[maxn],in[maxn];
int Hash[maxn];//PAT不能用hash 
node* preN[maxn];
int num = 0;//先序遍歷下標 



node* newNode(int x){
    node* w = new node;
    w->data =x;
    w->level = 1;
    w->lchild=w->rchild=w->parent=NULL;
    return w;
}

//根據當前子樹的前序排序序列和中序排序序列構建二叉樹 
node* create(int preL,int preR,int inL,int inR){
    if(preL>preR){//當前子樹爲空,沒有結點 
        return NULL;
    }
    node* root = newNode(pre[preL]);
    //首先找到中序遍歷序列中等於當前根結點的值得下標 
    int i;
    for(i=inL;i<=inR;++i){
        if(in[i]==pre[preL]){
            break;
        }
    } 
    int numLeft = i-inL; 
    //往左子樹插入,左子樹先序遍歷區間爲[preL+1,preL+numleft],中序遍歷區間爲[inL,i-1]
    root->lchild = create(preL+1,preL+numLeft,inL,i-1);
    //往右子樹插入,右子樹先序遍歷區間[preL+numLeft+1,preR],中序遍歷區間爲[i+1,inR]
    root->rchild = create(preL+numLeft+1,preR,i+1,inR);
    return root;

}
//先序遍歷
void tre(node* root){
    if(root==NULL) return;
    preN[num++] = root;
    tre(root->lchild);
    tre(root->rchild);
} 

//層序遍歷
void layerOrder(node* root){
    queue<node*> q;
    q.push(root);
    while(!q.empty()){
        node* w = q.front();
        q.pop();
        if(w->lchild!=NULL){
            w->lchild->level = w->level+1;
            w->lchild->parent = w;
            q.push(w->lchild); 
        } 
        if(w->rchild!=NULL){
            w->rchild->level = w->level+1;
            w->rchild->parent = w;
            q.push(w->rchild); 
        } 
    }
} 
int main(){
    int m,n;//測試的結點對數和結點數目 
    scanf("%d %d",&m,&n);
    memset(Hash,0,sizeof(Hash));
    for(int i=0;i<n;++i){
        scanf("%d",&in[i]);
    }
    for(int i=0;i<n;++i){
        scanf("%d",&pre[i]);
        Hash[pre[i]] = i+1;
    }
    node* root = create(0,n-1,0,n-1);
    layerOrder(root);
    //先序遍歷
    tre(root);
    //測試
    int x,y;
    for(int i=0;i<m;++i){
        scanf("%d %d",&x,&y);
        if(Hash[x]!=0&&Hash[y]!=0){
            //均是樹中結點
            node* w1 = preN[Hash[x]-1];//經過結點的值找到前序遍歷數組的下標,而後再找到對應結點 
            node* w2 = preN[Hash[y]-1]; 
            if(w1->level==w2->level){
                //2個結點在同一層
                if(w1==w2){
                    printf("%d is an ancestor of %d.\n",x,y);
                } else{
                    //2結點不相等,則同時往上走
                    node* t1 = w1;
                    node* t2 = w2;
                    while(t1->parent!=NULL){
                        t1 = t1->parent;
                        t2 = t2->parent;
                        if(t1==t2){
                            printf("LCA of %d and %d is %d.\n",x,y,t1->data);
                            break;
                        }
                    } 
                }
            }else{
                //2結點不在同一層,讓層數較大的先往上走 
                node* max = w1->level>w2->level?w1:w2;
                node* min = w1->level<w2->level?w1:w2;
                while(max->level!=min->level&&max->parent!=NULL){
                    max = max->parent;
                }
                //而後判斷min是否和max相等
                if(min==max){
                    //說明min是max的祖先,但此時max已經更改因此得從新賦值
                    max = w1->level>w2->level?w1:w2;
                    printf("%d is an ancestor of %d.\n",min->data,max->data); 
                } else{
                    //2結點不相等,則同時往上走
                    while(max->parent!=NULL){
                        max = max->parent;
                        min = min->parent;
                        if(max==min){
                            printf("LCA of %d and %d is %d.\n",x,y,min->data);
                            break;
                        }
                    } 
                }
            }
        }else if(Hash[x]==0&&Hash[y]!=0){
            printf("ERROR: %d is not found.\n",x);
        }else if(Hash[x]!=0&&Hash[y]==0){
            printf("ERROR: %d is not found.\n",y);
        }else{
            printf("ERROR: %d and %d are not found.\n",x,y);
        }
    } 
    return 0;
}
相關文章
相關標籤/搜索