二叉排序樹(BST)的判定(其實不容易)
阿新 • • 發佈:2019-02-09
對於BST,一定要理解透徹,下面,我們給出一個有錯誤的BST判定程式:
執行程式,發現結果是對的,但isBST函式在邏輯上是有硬傷的,不信請看isBST對下面這棵樹的驗證(結果為這棵非BST判為BST):// 程式中的isBST函式的邏輯是有錯誤 #include <iostream> #define N 7 using namespace std; // BST的結點 typedef struct node { int key; struct node *lChild, *rChild; }Node, *BST; // 在給定的BST插入element, 使之稱為新的BST bool BSTInsert(Node * &p, int element) { if(NULL == p) // 空樹 { p = new Node; p->key = element; p->lChild = p->rChild = NULL; return true; } if(element == p->key) // BST中不能有相等的值 return false; if(element < p->key) // 遞迴 return BSTInsert(p->lChild, element); return BSTInsert(p->rChild, element); // 遞迴 } // 建立BST void createBST(Node * &T, int a[], int n) { T = NULL; int i; for(i = 0; i < n; i++) { BSTInsert(T, a[i]); } } // 生成一個結點 Node *createNode(int i) { Node * q = new Node; q->lChild = NULL; q->rChild = NULL; q->key = i; return q; } // 建立一棵非BST樹(這是之前的程式碼,直接複製過來的) Node *createNotBST() { Node *p[N] = {NULL}; int i; for(i = 0; i < N; i++) p[i] = createNode(i + 1); for(i = 0; i < N/2; i++) { p[i]->lChild = p[i * 2 + 1]; p[i]->rChild = p[i * 2 + 2]; } return p[0]; } // 判斷是否為BST bool isBST(BST T) { // 空樹是BST if(NULL == T) return true; // 只有一個結點的數是BST樹 if(NULL == T->lChild && NULL == T->rChild) return true; // 左子樹為空(右子樹不為空) if(NULL == T->lChild) { if(T->key < T->rChild->key && isBST(T->rChild)) return true; return false; } // 右子樹為空(左子樹不為空) if(NULL == T->rChild) { if(T->key > T->lChild->key && isBST(T->lChild)) return true; return false; } // 左右子樹都非空 if(T->lChild->key < T->key && T->key < T->rChild->key && isBST(T->lChild) && isBST(T->rChild) ) return true; return false; } void printJudge(Node *T) { if(isBST(T)) cout << "yes" << endl; else cout << "no" << endl; } int main() { int a[10] = {4, 5, 2, 1, 0, 9, 3, 7, 6, 8}; int n = 10; BST T = NULL; printJudge(T); // yes // 並非所有的a[]都能構造出BST,所以,最好對createBST的返回值進行判斷 createBST(T, a, n); printJudge(T); // yes T = createNotBST(); printJudge(T); // no return 0; }
上述程式之所以有錯,主要是因為對BST的定義理解有誤,看看isBST函式就知道錯在何方。// 程式中的isBST函式的邏輯是有錯誤 #include <iostream> using namespace std; // BST的結點 typedef struct node { int key; struct node *lChild, *rChild; }Node, *BST; // 生成一個結點 Node *createNode(int i) { Node * q = new Node; q->lChild = NULL; q->rChild = NULL; q->key = i; return q; } /* * 下面這棵樹並非BST, 但程式中的isBST將其判為BST, 故isBST函式邏輯有誤 * 該樹為: 3 // 根節點為3 2 // 3的左孩子結點為2, 沒有右孩子結點 1 4 // 2的左孩子結點為1, 右孩子結點為4 */ Node *createTree() { Node *p1 = createNode(1); Node *p2 = createNode(2); Node *p3 = createNode(3); Node *p4 = createNode(4); p3->rChild = NULL; p3->lChild = p2; p2->lChild = p1; p2->rChild = p4; p1->lChild = p1->rChild = NULL; p4->lChild = p4->rChild = NULL; return p3; } // 判斷是否為BST bool isBST(BST T) { // 空樹是BST if(NULL == T) return true; // 只有一個結點的數是BST樹 if(NULL == T->lChild && NULL == T->rChild) return true; // 左子樹為空(右子樹不為空) if(NULL == T->lChild) { if(T->key < T->rChild->key && isBST(T->rChild)) return true; return false; } // 右子樹為空(左子樹不為空) if(NULL == T->rChild) { if(T->key > T->lChild->key && isBST(T->lChild)) return true; return false; } // 左右子樹都非空 if(T->lChild->key < T->key && T->key < T->rChild->key && isBST(T->lChild) && isBST(T->rChild) ) return true; return false; } void printJudge(Node *T) { if(isBST(T)) cout << "yes" << endl; else cout << "no" << endl; } int main() { BST T = NULL; printJudge(T); // yes T = createTree(); printJudge(T); // yes (isBST將該樹錯判為BST) return 0; }
下面,我們給出正確的程式(該程式沒有考慮結點值為INT_MIN的情形):
#include <iostream> #define N 7 using namespace std; int g_min = INT_MIN; // BST的結點 typedef struct node { int key; struct node *lChild, *rChild; }Node, *BST; // 生成一個結點 Node *createNode(int i) { Node * q = new Node; q->lChild = NULL; q->rChild = NULL; q->key = i; return q; } /* * 下面這棵樹並非BST, 但程式中的isBST將其判為BST, 故isBST函式邏輯有誤 * 該樹為: 3 // 根節點為3 2 // 3的左孩子結點為2, 沒有右孩子結點 1 4 // 2的左孩子結點為1, 右孩子結點為4 */ Node *createTree() { Node *p1 = createNode(1); Node *p2 = createNode(2); Node *p3 = createNode(3); Node *p4 = createNode(4); p3->rChild = NULL; p3->lChild = p2; p2->lChild = p1; p2->rChild = p4; p1->lChild = p1->rChild = NULL; p4->lChild = p4->rChild = NULL; return p3; } // 建立一棵非BST樹 Node *createNotBST() { Node *p[N] = {NULL}; int i; for(i = 0; i < N; i++) p[i] = createNode(i + 1); for(i = 0; i < N/2; i++) { p[i]->lChild = p[i * 2 + 1]; p[i]->rChild = p[i * 2 + 2]; } return p[0]; } // 在給定的BST插入element, 使之稱為新的BST bool BSTInsert(Node * &p, int element) { if(NULL == p) // 空樹 { p = new Node; p->key = element; p->lChild = p->rChild = NULL; return true; } if(element == p->key) // BST中不能有相等的值 return false; if(element < p->key) // 遞迴 return BSTInsert(p->lChild, element); return BSTInsert(p->rChild, element); // 遞迴 } // 建立BST void createBST(Node * &T, int a[], int n) { T = NULL; int i; for(i = 0; i < n; i++) { BSTInsert(T, a[i]); } } // 判斷是否為BST bool isBST(BST T) { if(NULL != T) { isBST(T->lChild); if(T->key <= g_min) return false; g_min = T->key; isBST(T->rChild); } return true; } void printJudge(Node *T) { if(isBST(T)) cout << "yes" << endl; else cout << "no" << endl; } int main() { int a[10] = {4, 5, 2, 1, 0, 9, 3, 7, 6, 8}; int n = 10; BST T = NULL; g_min = INT_MIN; printJudge(T); // yes T = createTree(); g_min = INT_MIN; printJudge(T); // no T = createNotBST(); g_min = INT_MIN; printJudge(T); // no createBST(T, a, n); g_min = INT_MIN; printJudge(T); // yes return 0; }
上面這個程式的原理是什麼呢?怎麼感覺有點類似於中序遍歷呢?確實如此,其實就是中序遍歷,原理是:
(1) 空二叉樹是BST
(2) 對於非空二叉樹而言:中序遍歷為嚴格遞增數列 《==》該樹為BST.