Xbox菲爾·斯賓塞表示 希望能延續《殺手本能》系列
笛卡爾樹
笛卡爾樹是一種特殊的二叉樹,其結點包含兩個關鍵字K1和K2。首先笛卡爾樹是關於K1的二叉搜尋樹,即結點左子樹的所有K1值都比該結點的K1值小,右子樹則大。其次所有結點的K2關鍵字滿足優先佇列(不妨設為最小堆)的順序要求,即該結點的K2值比其子樹中所有結點的K2值小。給定一棵二叉樹,請判斷該樹是否笛卡爾樹。
輸入格式:
輸入首先給出正整數N(≤1000),為樹中結點的個數。隨後N行,每行給出一個結點的資訊,包括:結點的K1值、K2值、左孩子結點編號、右孩子結點編號。設結點從0~(N-1)順序編號。若某結點不存在孩子結點,則該位置給出−1。
輸出格式:
輸出YES如果該樹是一棵笛卡爾樹;否則輸出NO
輸入樣例1:
6 8 27 5 1 9 40 -1 -1 10 20 0 3 12 21 -1 4 15 22 -1 -1 5 35 -1 -1
輸出樣例1:
YES
輸入樣例2:
6 8 27 5 1 9 40 -1 -1 10 20 0 3 12 11 -1 4 15 22 -1 -1 50 35 -1 -1
輸出樣例2:
NO
解題思路
這道題沒什麼難度,判斷是否是搜尋樹和最小堆寫個遞迴來實現就好了。
記錄的原因是因為我又忽略了判斷搜尋樹的條件了...我又犯的錯誤是隻用根節點的值來與左子樹的根節點的值和右子樹根節點的值比較,如果根節點比左子樹的根節點小,比右子樹的根節點大,就說明這顆書是BST。實際上這種判斷方法是很有問題的,比如下圖:
如果按照上面的方法來判斷,無論是先序、中序還是後序,都會判定為BST,但實際上它並不是BST。
正確的判斷方法是,根節點的值比左子樹所有節點的值大,比右子樹所有節點的值小,因此還需要再寫個遞迴程式碼來做這件事情。
AC程式碼如下:
1 #include <cstdio> 2 using namespace std; 3 4 const int MAXN = 1001; 5 6 struct TNode { 7 int k1, k2; 8 int left, right; 9 }tree[MAXN]; 10 11 bool judge(intk1, int root, bool flag) { 12 if (root == -1) return true; 13 if (flag) { 14 if (k1 > tree[root].k1) return judge(k1, tree[root].left, flag) && judge(k1, tree[root].right, flag); 15 else return false; 16 } 17 else { 18 if (k1 < tree[root].k1) return judge(k1, tree[root].left, flag) && judge(k1, tree[root].right, flag); 19 else return false; 20 } 21 } 22 23 bool isBST(int root) { 24 if (root == -1) return true; 25 if (judge(tree[root].k1, tree[root].left, true) && judge(tree[root].k1, tree[root].right, false)) return isBST(tree[root].left) && isBST(tree[root].right); 26 else return false; 27 } 28 29 bool isMinHeap(int root) { 30 if (root == -1) return true; 31 if (isMinHeap(tree[root].left) && isMinHeap(tree[root].right)) { 32 if (tree[root].left != -1 && tree[root].k2 > tree[tree[root].left].k2) return false; 33 if (tree[root].right != -1 && tree[root].k2 > tree[tree[root].right].k2) return false; 34 else return true; 35 } 36 else return false; 37 } 38 39 int main() { 40 int n; 41 scanf("%d", &n); 42 bool notRoot[n] = {false}; 43 for (int i = 0; i < n; i++) { 44 int k1, k2, left, right; 45 scanf("%d %d %d %d", &k1, &k2, &left, &right); 46 tree[i] = {k1, k2, left, right}; 47 if (left != -1) notRoot[left] = true; 48 if (right != -1) notRoot[right] = true; 49 } 50 51 int root = 0; 52 while (notRoot[root]) { 53 root++; 54 } 55 printf("%s", isBST(root) && isMinHeap(root) ? "YES" : "NO"); 56 57 return 0; 58 }
還有一種判斷是否是BST的方法,就是利用BST的特性,根據關鍵字K1進行中序遍歷,將得到的序列進行比較,如果這個序列是遞增的,就說明這顆樹是BST,如果發現某個值比它後一個值大,就說明不是BST。
一開始沒有想到這種方法,真的是太妙了。
AC程式碼如下:
1 #include <cstdio> 2 #include <vector> 3 using namespace std; 4 5 const int MAXN = 1001; 6 vector<int> in; 7 8 struct TNode { 9 int k1, k2; 10 int left, right; 11 }tree[MAXN]; 12 13 void inOrderTraversal(int root) { 14 if (root != -1) { 15 inOrderTraversal(tree[root].left); 16 in.push_back(tree[root].k1); 17 inOrderTraversal(tree[root].right); 18 } 19 } 20 21 bool isBST(int root) { 22 inOrderTraversal(root); 23 for (int i = 0; i < in.size() - 1; i++) { 24 if (in[i] > in[i + 1]) return false; 25 } 26 return true; 27 } 28 29 bool isMinHeap(int root) { 30 if (root == -1) return true; 31 if (isMinHeap(tree[root].left) && isMinHeap(tree[root].right)) { 32 if (tree[root].left != -1 && tree[root].k2 > tree[tree[root].left].k2) return false; 33 if (tree[root].right != -1 && tree[root].k2 > tree[tree[root].right].k2) return false; 34 else return true; 35 } 36 else return false; 37 } 38 39 int main() { 40 int n; 41 scanf("%d", &n); 42 bool notRoot[n] = {false}; 43 for (int i = 0; i < n; i++) { 44 int k1, k2, left, right; 45 scanf("%d %d %d %d", &k1, &k2, &left, &right); 46 tree[i] = {k1, k2, left, right}; 47 if (left != -1) notRoot[left] = true; 48 if (right != -1) notRoot[right] = true; 49 } 50 51 int root = 0; 52 while (notRoot[root]) { 53 root++; 54 } 55 printf("%s", isBST(root) && isMinHeap(root) ? "YES" : "NO"); 56 57 return 0; 58 }
參考資料
資料結構與演算法題目集7-31——笛卡爾樹:https://blog.csdn.net/qq_41231926/article/details/84888050