L2-004 這是二叉搜索樹嗎? (25 分)
一棵二叉搜索樹可被遞歸地定義為具有下列性質的二叉樹:對於任一結點,
其左子樹中所有結點的鍵值小於該結點的鍵值;
其右子樹中所有結點的鍵值大於等於該結點的鍵值;
其左右子樹都是二叉搜索樹。
所謂二叉搜索樹的“鏡像”,即將所有結點的左右子樹對換位置后所得到的樹。
給定一個整數鍵值序列,現請你編寫程序,判斷這是否是對一棵二叉搜索樹或其鏡像進行前序遍歷的結果。
輸入格式:
輸入的第一行給出正整數 N(≤1000)。隨后一行給出 N 個整數鍵值,其間以空格分隔。
輸出格式:
如果輸入序列是對一棵二叉搜索樹或其鏡像進行前序遍歷的結果,則首先在一行中輸出 YES ,然后在下一行輸出該樹后序遍歷的結果。數字間有 1 個空格,一行的首尾不得有多余空格。若答案是否,則輸出 NO。
輸入樣例 1:
7
8 6 5 7 10 8 11
輸出樣例 1:
YES
5 7 6 8 11 10 8
輸入樣例 2:
7
8 10 11 8 6 7 5
輸出樣例 2:
YES
11 8 10 7 5 6 8
輸入樣例 3:
7
8 6 8 5 10 9 11
輸出樣例 3:
NO
這題用遞歸的方法嘗試進行建樹,如果出現相駁就能返回false,文中有注釋幫助理解,如有問題可直接評論。
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.StreamTokenizer;
import java.util.Scanner;
public class Main {
static StreamTokenizer st = new StreamTokenizer(new BufferedInputStream(System.in));
static int out[] = new int[1005];// 后序遍歷結果
static int next;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = nextNum();
next = N;
if (N == 0) {
System.out.println("YES");
return;
}
int arr[] = new int[1005];
for (int i = 0; i < N; i++) {
arr[i] = nextNum();
}
if (build(arr, 0, N, true, arr[0])) {
System.out.println("YES");
System.out.print(out[0]);
for (int i = 1; i < N; i++) {
System.out.print(" " + out[i]);
}
} else {
next = N;
if (buildjx(arr, 0, N, true, arr[0])) {
System.out.println("YES");
System.out.print(out[0]);
for (int i = 1; i < N; i++) {
System.out.print(" " + out[i]);
}
} else {
System.out.println("NO");
}
}
}
/** * 判斷能否代表二叉排序樹 * @param arr 二叉樹組 * @param l 左起始 * @param r 有終點 * @param lr 代表該樹是左節點還是右節點,true為左節點 * @param preRoot 為該樹父節點 * @return 判斷結果 */
private static boolean build(int arr[], int l, int r, boolean lr, int preRoot) {
if (l >= r) {
return true;
}
int root = arr[l];
out[--next] = root;// 存儲其根
if (lr) {
for (int i = l + 1; i < r; i++) {
if (root <= arr[i]) {// 當出現大於等於該根的時候,可認為剩余的為右子樹
// 后根遍歷中有子樹根節點后出所以先遞歸右節點,再遞歸左節點
return build(arr, i, r, false, root) && build(arr, l + 1, i, true, root);
}
}
} else {
for (int i = l + 1; i < r; i++) {// 由於右子樹都大於等於根,出現小於的時候,必然不能代表二叉樹
if (arr[i] < preRoot) {
return false;
}
}
for (int i = l + 1; i < r; i++) {
if (root <= arr[i]) {
return build(arr, i, r, false, root) && build(arr, l + 1, i, true, root);
}
}
}
return build(arr, l + 1, r, true, root);
}
/** * 判斷能否代表二叉排序樹 和判斷是否代表二叉排序樹基本相同,只是變了下符號 * @param arr 二叉樹組 * @param l 左起始 * @param r 有終點 * @param lr 代表該樹是左節點還是右節點,true為左節點 * @param preRoot 為該樹父節點 * @return 判斷結果 */
private static boolean buildjx(int arr[], int l, int r, boolean lr, int preRoot) {
if (l >= r) {
return true;
}
int root = arr[l];
out[--next] = root;
if (lr) {
for (int i = l + 1; i < r; i++) {
if (root > arr[i]) {
return buildjx(arr, i, r, false, root) && buildjx(arr, l + 1, i, true, root);
}
}
} else {
for (int i = l + 1; i < r; i++) {
if (arr[i] > preRoot) {
return false;
}
}
for (int i = l + 1; i < r; i++) {
if (root > arr[i]) {
return buildjx(arr, i, r, false, root) && buildjx(arr, l + 1, i, true, root);
}
}
}
return buildjx(arr, l + 1, r, true, root);
}
private static int nextNum() {
try {
st.nextToken();
} catch (IOException e) {
e.printStackTrace();
}
return (int) st.nval;
}
}