關於二叉樹已知中序和後序遍歷求前序遍歷問題
阿新 • • 發佈:2022-04-20
我們先來看一張圖
這裡我們只關注中序和後序遍歷對於對應子樹的根節點及其左子樹和右子樹的劃分。
可以看到中序遍歷
的整體情況是 左子樹
根
右子樹
,而後序遍歷
則是左子樹
右子樹
根
。
從中我們可以瞭解到第一個性質:任意一顆樹的後序遍歷的最後一個節點一定是當前樹的根
。
有了這個性質我們就可以找到當前子樹的根了,那麼對於子樹的前序和後續遍歷序列我們又該如何處理?
別急,繼續觀察前序和後序遍歷的序列,我們還可以發現第二個性質
對一棵樹進行中序和後序遍歷產生的序列,
其左子樹和右子樹在對應序列中的分佈都是是連續的
接下來,我們將左右兩顆子樹拿下來分別當成獨立的一棵樹來看(相應的後序與中序遍歷序列與上圖對應)
左子樹
右子樹
可以發現我們之前發現的兩個性質對於一棵樹的子樹也是同樣適用的(樹是遞迴定義的),那麼在我們找完一顆樹的根節點後,想要得到前序遍歷的結果,只需要在對樹進行中序和後序遍歷產生的序列中分別找到其左子樹
和右子樹
的部分繼續遞迴查詢即可。
由此我們可以大概得出這樣一個流程
我們先將該子樹在中序遍歷序列中的範圍表示為
[left(左端點), right(右端點)]
在後續遍歷序列中最後一個元素的位置用last表示。
- 輸出該樹(子樹)的後序遍歷的最後一個元素(即該樹(子樹)的根節點)
下面的描述將省略主體 -- 當前子樹
- 找到後續遍歷的最後一個元素(即根節點)在對應中序遍歷序列中的位置(記為pov),則對應左右子樹在中序遍歷序列中的範圍為
[left, pov - 1]
[pov + 1, right]
- 分別在後續遍歷序列中找到左子樹的last,記為l_last, 右子樹的last, 記為r_last
- 將左右子樹當成獨立的樹看待(性質二的區間連續保證了可以這樣做),將找到的左右子樹在中序和後序遍歷序列中的範圍和位置作為各自的引數重複執行第一步。
在第三步中右子樹的r_last顯然是 last - 1。
左子樹的l_last經過觀察可以發現,它的位置距離相對last(此處非左子樹的l_last)隔了一個右子樹的大小
因此 l_last 的位置就是 last - 右子樹大小 - 1
--> last - (right - (pov + 1) + 1) - 1 = last - right + pov - 1
接下來就是程式碼實現拉~~~
#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_map>
using namespace std;
const int N = 50;
unordered_map<int, int> m;
int flect[N], hash_idx = 0;
int hash_(int x){
//因為題目給出的樹的節點編號不一定連續所以做了下離散化
if(!m[x]){
m[x] = ++hash_idx;
flect[hash_idx] = x;
}
return m[x];
}
int ans[N], idx;
int mid[N], post[N];
int flectmid[N];//對應的值在中序遍歷序列中的位置
// l, r 當前子樹在mid陣列中的範圍
// p 當前子樹的根節點在post陣列中的位置
void dfs(int l, int r, int p){
if(l > r || p <= 0) return;
printf(" %d", flect[post[p]]);
int pov = flectmid[post[p]];
//左子樹
dfs(l, pov - 1, p - (r - pov + 1));
//右子樹
dfs(pov + 1, r, p - 1);
}
int main(){
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", post + i);
post[i] = hash_(post[i]);
}
for(int i = 1; i <= n; i++){
scanf("%d", mid + i);
mid[i] = hash_(mid[i]);
flectmid[mid[i]] = i;
}
printf("Preorder:");
dfs(1, n, n);
}