爆刷PAT(甲級)——之【1119 】Pre- and Post-order Traversals (30 分)——先序後序建立中序遍歷

 題意:分別輸入一個先序和後序遍歷的序列,給出對應的中序遍歷,並判斷此中序遍歷是否唯一。

 

方法與學習過程:本題就是赤裸裸的如何由先序和後序遍歷,進行建樹or進行中序遍歷。學習了三位博客的內容,我就不獻醜贅述了~

柳神的代碼是用來膜的,看一下柳神的宏觀思路:https://blog.csdn.net/liuchuo/article/details/52505179

這位兄弟的博客主要是由圖文的一個描述,可以更好的理解什麼情況下,中序遍歷是不唯一的?但是代碼就比較冗長了...https://blog.csdn.net/li1615882553/article/details/88061615

這位道友的博客主要是和柳神一樣,代碼寫的很好噢!37行代碼,思路清晰而且很通俗易懂,本題代碼我是借鑑了他的。https://blog.csdn.net/rain722/article/details/52596149

 

 思考:學習了【先序後序建樹得中序】之後呢,自然而然會將其與【先序中序建樹】與【後序中序建樹】進行聯想。這其中的代碼撰寫與思考是有一定的異同的,我之前總結了【先序中序建樹】與【後序中序建樹】的建樹模板與思路傳送門在這裏PAT上也有道題目,是【先序+後序】進行建樹的傳送門2在這裏

首先【先序中序建樹】與【後序中序建樹】的思考方式,都是先找出根節點在中序中的位置,然後就可以對左右子樹進行遞歸了!故根節點可以在 “先序/中序” 與 “中序” 建立聯繫,因此切入點是——根節點

而本題目的【先序後序建樹得中序】中,沒有中序呀!而且根的位置很顯然,就是先序的第一個唄。但是我們還是要想辦法剝離出左右子樹進行遞歸,所以我們從“某個子樹的根”來切入,即”當前根節點的孩子“來切入因爲 孩子節點能夠在 ”先序“ 和 “後序” 中建立聯繫。這一點很重要。在下面的代碼中,體現這個思想,是在getIn(...)函數的for循環裏,找到孩子在後序中的位置

而我們在建立中序遍歷的輸出序列時候,秉持的依然是“左根右”的方式,所以先對左子樹進行遍歷,再寫入根於in數組中,再對右子樹進行遍歷。

不論是之前的【先序中序建樹】與【後序中序建樹】,還是本次的【先序後序建樹得中序】建樹,核心都在於,根據已有的序列,如何把序列分辨出哪個是根,哪個是左子樹的部分,哪個是右子樹的部分,就好了,剩下的交給計算機去遞歸就行了!

 

Code:借鑑了第三位博主的寫法,再加上自己的書寫習慣,第一次寫PAT能有30來行的代碼,哭惹 T T

 

#include<cstdio>
#define inf 39
int n,pre[inf],in[inf],post[inf],pos;
bool getIn(int l1,int r1,int l2,int r2)//此時根是pre[l1],而pre[l1+1]是它的孩子
{
    if(l1>r1)return false;
    if(l1==r1)//爲什麼要特判?否則下面對孩子的訪問會越界段錯誤的
    {
        in[pos++]=pre[l1];//此處是根不是孩子噢
        return true;
    }
    int i;
    for(i=l2; post[i]!=pre[l1+1]; i++); //找出第一個孩子
    int len=i-l2+1;//由於包含此孩子節點,務必要加1
    int tag=1;
    tag&=getIn(l1+1,l1+len,l2,i);
    in[pos++]=pre[l1];//中序遍歷的精華,此處是根不是孩子噢
    tag&=getIn(l1+len+1,r1,i+1,r2-1);
    return tag;
}
int main()
{
    scanf("%d",&n);
    for(int i=0; i<n; i++)scanf("%d",&pre[i]);
    for(int i=0; i<n; i++)scanf("%d",&post[i]);
    pos=0;//pos是in統計的下標
    if(getIn(0,n-1,0,n-1))printf("Yes\n");
    else printf("No\n");
    for(int i=0; i<n; i++)printf("%d%c",in[i],i==n-1?'\n':' ');
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章