描述
在參與過了美食節之後,小Hi和小Ho在別的地方又玩耍了一陣子,在這個過程中,小Ho得到了一個非常有意思的玩具——一棵由小球和木棍連接起來的二叉樹!
小Ho對這棵二叉樹愛不釋手,於是給它的每一個節點都標記了一個標號——一個屬於A..Z的大寫字母,並且沒有任意兩個節點的標號是一樣的。小Hi也瞅準了這個機會,重新鞏固了一下小Ho關於二叉樹遍歷的基礎知識~就這樣,日子安穩的過了兩天。
這天,小Ho正好在求解這棵二叉樹的前序、中序和後序遍歷的結果,但是卻在求出前序遍歷和中序遍歷之後不小心把二叉樹摔到了地上,小球和木棍等零件散落了一地!
小Ho損失了心愛的玩具,正要嚎啕大哭起來,所幸被小Hi發現了,勸說道:“彆着急,這不是零件都還在麼?拼起來不就是了?”
“可是我忘記了二叉樹長什麼樣子了!”小Ho沮喪道。
“這個簡單,你不是剛剛求出了這棵二叉樹的前序和中序遍歷的結果麼,利用這兩個信息就可以還原出整棵二叉樹來哦!”
“這樣麼?!!”小Ho止住了淚水,問道:“那要怎麼做呢?”
沒錯!小Ho在這一週遇到的問題便是:給出一棵二叉樹的前序和中序遍歷的結果,還原這棵二叉樹並輸出其後序遍歷的結果。
提示:分而治之——化大爲小,化小爲無
×Close
提示:分而治之——化大爲小,化小爲無
“這可就要從頭說起了,我們先找一棵二叉樹作爲例子吧!”小Hi在紙上畫了畫,遞給了小Ho。
2.jpg
“不妨假設你的二叉樹長成這個樣子~”小Hi道。
“可是我的二叉樹並不是長成這個樣子啊!雖然我讀書少,但是你不要騙我好不好!”小Ho一臉氣憤道。
“我都說了是假設!是爲了能讓你明白如何通用的去解決這樣的問題!”小Hi無奈道。
“好吧……你接着說。”小Ho算是認可了這個說法。
“那麼對於這棵二叉樹,你先來計算一下它的前序遍歷和中序遍歷的結果吧!”小Hi也是毫不含糊就開始下發任務。
“唔……前序遍歷是ABDEGHCFIJ,中序遍歷……我想想啊……是DBGEHACIJF!”小Ho並沒有花費多長時間就給出了答案。
“你還記得前序遍歷的遞歸定義麼?”小Hi點了點頭又繼續問道。
“是‘根節點’+‘左子樹的前序遍歷’+‘右子樹的前序遍歷’!”小Ho毫不猶豫的答道:“而在這裏體現出來就是‘A'+‘BDEGH’+‘CFIJ’”。
“那中序遍歷呢?”小Hi繼續問道。
“是‘左子樹的中序遍歷’+‘根節點’+‘右子樹的中序遍歷’!在這裏也就是‘DBGEH’+‘A’+‘CIJF’。”小Ho這次花的時間更少了。
“還記得動態規劃時候我們一般處理的方法麼?我們這裏也可以這樣做哦,如果我們定義post_order(str1, str2)爲一棵前序遍歷的結果爲str1,中序遍歷的結果爲str2的二叉樹的後序遍歷的結果的話,我們有沒有辦法把它化解成爲子問題呢?”小Hi道。
“讓我想想……”小Ho拿出紙筆開始演算起來:“如果我要求解post-order(str1, str2)的話,首先不難發現,根據‘前序遍歷’str1=‘根節點’+‘左子樹的前序遍歷’+‘右子樹的前序遍歷’,我可以知道這棵二叉樹的根節點root便是str1的第一個字符!”小Ho道。
“而我在知道了‘根節點’root之後,我便可以利用‘中序遍歷’str2=‘左子樹的中序遍歷’+‘根節點’+‘右子樹的中序遍歷’,求解出‘左子樹的中序遍歷’str2L和‘右子樹的中序遍歷’str2R!”
“接下來,由於一棵子樹的前序遍歷和中序遍歷的長度相同,那麼仍然是根據‘前序遍歷’str1=‘根節點’+‘左子樹的前序遍歷’+‘右子樹的前序遍歷’,我可以知道從str1的第2個字符開始的str2L.length()個字符便是‘左子樹的前序遍歷’str1L,而這之後的部分便是‘右子樹的前序遍歷’str1R!”小Ho說到這裏,頓了頓,希望從小Hi處得到些反饋。
“沒錯!那你準備如何求解post_order(str1, str2)呢?”小Hi肯定道。
“這隻要根據之前求出的結果,和‘後序遍歷’=‘左子樹的後序遍歷’+‘右子樹的後序遍歷’+‘根節點’,便可以知道——post_order(str1, str2)=post_order(str1l, str2l)+post_order(str1r, str2r)+root這樣一個式子來!而這個問題的規模——也就是二叉樹的大小,是一直在縮小的,所以是能夠這樣不斷的劃分做下去的!也就是說我之後可以將當前根節點的左子樹又拆分成爲兩個問題來解決,一直到當前節點是葉子節點了爲止!然後慢慢的將這些答案拼成上層的問題的答案,而這個過程只需要使用遞歸完成就可以了!”
“聽起來很容易的樣子呢!那你要不要趕緊去實現了算法,算出你的寶貝二叉樹長什麼樣呢?”小Hi滿意的點了點頭,隨即笑着問道。
“那是自然!”
Close
輸入
每個測試點(輸入文件)有且僅有一組測試數據。
每組測試數據的第一行爲一個由大寫英文字母組成的字符串,表示該二叉樹的前序遍歷的結果。
每組測試數據的第二行爲一個由大寫英文字母組成的字符串,表示該二叉樹的中序遍歷的結果。
對於100%的數據,滿足二叉樹的節點數小於等於26。
輸出
對於每組測試數據,輸出一個由大寫英文字母組成的字符串,表示還原出的二叉樹的後序遍歷的結果。
題目大意:給出一二叉樹的前序和中序遍歷,要求輸出後序遍歷。
大致思路:
由前序遍歷的第一個字母得到當前樹的父節點,然後查找中序遍歷中的這個父節點,在左邊的就是中序左子樹,右面的是中序右子樹。根據左右子樹的長度,可以得到前序遍歷中的前序左子樹和前序右子樹。
因爲棧是後進先出,所以最後如果想按左子樹右子樹父節點的順序出棧得到後序遍歷,那麼入棧的時候就要按父節點右子樹左子樹的順序。
先將得到的父節點入棧,然後遞歸調用本函數入棧右子樹(參數爲前序右子樹,中序右子樹),再入棧左子樹。
遞歸函數的結束條件是傳進來的樹爲空,及說明最小單位的子樹已經完成入棧。
AC代碼:
#include<iostream>
#include<cstdio>
#include<stack>
#include<string>
using namespace std;
stack<char> stk;
void stack_push(string pre,string mid)
{
if(pre.length()==0)return;
string prelefttree,prerighttree,midlefttree,midrighttree;
char root=pre[0];
stk.push(root);//父節點入棧
int rootpos=0;
for(;rootpos<mid.length();rootpos++)
if(mid[rootpos]==root)break;
int rightsize=mid.length()-1-rootpos;
int leftsize=mid.length()-1-rightsize;
for(int i=0;i<leftsize;i++)
{
prelefttree+=pre[1+i];
midlefttree+=mid[i];
}
for(int i=0;i<rightsize;i++)
{
prerighttree+=pre[leftsize+1+i];
midrighttree+=mid[rootpos+1+i];
}
stack_push(prerighttree,midrighttree);//右子樹入棧
stack_push(prelefttree,midlefttree);//左子樹入棧
}
int main()
{
string pre,mid;
while(cin>>pre)
{
cin>>mid;
while(!stk.empty())stk.pop();
stack_push(pre,mid);
while(!stk.empty())
{
cout<<stk.top();
stk.pop();
}
cout<<endl;
}
}