1. 程式人生 > >3965 Binary Tree Restoring (dfs)

3965 Binary Tree Restoring (dfs)

題目

題意

給出一顆二叉樹的兩種 dfsdfs 序列,即同一層上沒有明確的左右子樹概念,兩種序列可以以根節點-左/右子樹-另一顆子樹的順序實現。求一顆滿足兩種 dfsdfs 序列的二叉樹,輸出每一個節點的父親節點。題目保證有解

思路

因為是一顆樹的重構,我們可以同時從兩棵樹的 dfsdfs 序列入手,我們可以分別在兩個序列上,找到當前子樹所在的區間,並且可以通過判斷當前位在兩個序列中的值是否相同,來判斷當前父親節點需要兩棵子樹還是一棵子樹。 原因是這樣的,因為兩個 dfsdfs 序列,是以這樣的形勢展現的: rootlsroot - ls _ rootl

srsroot - ls - rs _ rootrsroot - rs rootrsroot - rs _ rootrslsroot - rs - ls _ rootlsroot - lsrootlsroot - ls _ rootlsrsroot - ls - rs _ rootrsroot - rs rootlsroot - ls _ rootlsrsroot - ls - rs _ rootr
sroot - rs
那麼當我們確定一個根後,如果他們的後一個節點是相同的,我們可以判斷後一個節點引導了一個連在當前根的子樹。如果當前根的後一個值在兩個序列中是不同的,那麼這兩個值各自引導了一個連在當前根的子樹。兒通過查詢兩個值的兩個序列中的位置,我們可以確定子樹所在的序列,然後遞迴呼叫。 需要注意的一點是,因為我們的樹是二叉的,所以當前根有兩棵子樹時,是不用考慮的,因為子樹長度是確定的,而如果當前根只有一棵子樹的話,我們需要判斷是否存在另一顆子樹。

程式碼

遞迴呼叫實現

const int maxn = 1e5+5;
int a[maxn], b[maxn], pa[maxn]
, pb[maxn]; int pre[maxn]; void build(int root, int la, int lb, int &len){ if(len == 0) return; if(a[la] != b[lb]){ pre[a[la]] = root; pre[b[lb]] = root; // la-ls, lb-rs; int ls_la = la+1, ls_ra = pa[b[lb]]-1; int ls_len = ls_ra - ls_la + 1; int rs_lb = lb+1, rs_rb = pb[a[la]]-1; int rs_len = rs_rb - rs_lb + 1; int ls_lb = pb[a[la]]+1; int rs_la = pa[b[lb]]+1; build(a[la], ls_la, ls_lb, ls_len); // ls build(b[lb], rs_la, rs_lb, rs_len); // rs len = ls_len + rs_len + 2; return ; } else { pre[a[la]] = root; int ls_la = la+1, ls_lb = lb+1, ls_len = len-1; build(a[la], ls_la, ls_lb, ls_len); if(ls_len+1 < len){ int rs_root = a[la+ls_len+1], rs_la = la+ls_len+2, rs_lb = lb+ls_len+2; int rs_len = len - ls_len - 2; pre[rs_root] = root; build(rs_root, rs_la, rs_lb, rs_len); len = ls_len + rs_len + 2; } } } int main() { int T, n; sd(T); while(T--){ sd(n); rep(i, 0, n) { sd(a[i]); pa[a[i]] = i; } rep(i, 0, n) { sd(b[i]); pb[b[i]] = i; } build(0, 0, 0, n); rep(i, 1, n+1){ printf("%d%c", pre[i], i==n?'\n':' '); } } return 0; }