題目來源:團體程序設計天梯賽-練習集
題目地址:L2-004 這是二叉搜索樹嗎?
題目大意
給定一個長度爲 的序列,判斷這是否是對一棵二叉搜索樹或其鏡像進行前序遍歷的結果。如果是,則在一行中輸出 YES ,然後在下一行輸出該樹後序遍歷的結果,否者直接輸出 NO。
題目分析
1. 預備知識
前序遍歷:先訪問根節點,再遍歷左子樹,最後遍歷右子樹。
後序遍歷:先遍歷左子樹,再遍歷右子樹,最後訪問根節點。
(注意:遍歷子樹的時候也要按照相應的的方式遍歷。)
二叉搜索樹的基本性質(如題目描述所示)
2. 結題要義
首先,我們假設輸入的序列 就是一棵二叉搜索樹進行前序遍歷的結果, 和 分別爲序列 的左右邊界。
根據前序遍歷的性質,我們可以知道 即爲這棵樹的根節點、 爲左子樹的左邊界, 爲右子樹的右邊界。然後我們定義兩個指針 、 分別表示右子樹的左邊界、左子樹的右邊界。(注意哪個對應哪個)
初始化 、,接着根據二叉樹搜索樹的性質 , 往右移動,找到第一個大於或等於根節點 的節點、往右移動,找到第一個小於根節點 的節點。過程如下圖所示,
這樣就確定了序列中根節點、左子樹和右子樹的位置,我們遞歸進行這個這個過程,就可以得到整棵樹的結構,過程如下圖所示:
爲了簡便,我們可以省去建樹的過程,在確定了序列中根節點、左子樹和右子樹的位置後,就直接進行後序遍歷。由確定子樹範圍的過程可得,若這是一顆二叉搜索樹,則必有 ,要是不滿足這個條件,我們就直接停止遍歷。
訪問根節點時,我們可以將根節點放入 中(即爲後序遍歷序列)。最後我們通過判斷 中的元素個數是否等於 來判斷這是否爲二叉搜索樹。
如果等於 ,就可以輸出 ;否則就判斷是否爲鏡像。至於求判斷鏡像的過程也基本和上訴無異,區別在於確定子樹範圍的條件而已。
代碼如下
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
int n, flag;
/**
* a[]用於存儲輸入的前序遍歷序列
* ans用於存儲後序遍歷的結果
*/
int a[maxn];
vector<int> ans;
/**
* 後序遍歷這顆二叉樹
*
*/
void dfs(int l, int r) {
if (l > r) return ;
//tl表示右子樹的左邊界,tr表示左子樹的右邊界
int tl = l + 1, tr = r;
if (flag) {
//判斷二叉搜索樹的“鏡像”
while (tl <= r && a[tl] >= a[l]) tl++;
while (tr > l && a[tr] < a[l]) tr--;
} else {
//判斷二叉搜索樹
while (tl <= r && a[tl] < a[l]) tl++;
while (tr > l && a[tr] >= a[l]) tr--;
}
//不滿足二叉搜索樹的條件
if (tl - tr != 1) return ;
//訪問左子樹
dfs(l + 1, tr);
//訪問右子樹
dfs(tl, r);
//訪問根節點
ans.push_back(a[l]);
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
//先檢查是不是二叉搜索樹的“鏡像”
dfs(1, n);
//如果不滿足,則再檢查是不是二叉搜索樹
if (ans.size() != n) {
flag = 1;
ans.clear();
dfs(1, n);
}
if (ans.size() != n) {
cout << "NO" << endl;
} else {
cout << "YES" << endl;
for (int i = 0; i < n; i++) {
cout << ans[i] << (i == n -1 ? '\n' : ' ');
}
}
return 0;
}
如果本文對你有所幫助,記得點個贊哦~