1、什麼是二叉查找樹算法
二叉查找樹(Binary Search Tree)是一種特殊的二叉樹,對於一個二叉查找樹,樹中的每一個結點X,它的左子樹中全部關鍵字的值都小於X的關鍵字值;而它的右子樹中全部關鍵字的值大於X的關鍵字值。這意味着,該樹的全部元素可使用一種統一的方式進行排序,所以,二叉查找樹又稱爲二叉排序樹。下圖即爲一個二叉查找樹:數據結構
2、如何在 BST 中查找一個結點spa
二叉查找樹很適合進行查找操做。以上圖中的二叉查找樹爲例:若是須要在該 BST 中查找值 10,先從根結點開始進行比較,10小於13,根據 BST 的性質,能夠知道若是該 BST 中有結點值爲10的話,那麼該結點必然位於根結點的左子樹中;再從結點 5 開始,可知 10 在結點 5 的右子樹;再從結點 8 開始,10 在結點 8 的右子樹,最終找到元素 10。code
用算法來描述,在一個 二叉查找樹 T(T 爲二叉樹的根結點)中查找元素 n:blog
1)若 T 爲空,則 n 不在 BST 中;排序
2)判斷 n 與根結點值(T->val)的關係;遞歸
3)若是 n 等於 T->val,則找到了元素 n;內存
4)若是 n 大於 T->val,則去根結點的右子樹 (T->right) 繼續查找。將 T->right 做爲 T,回到第 1 步;element
5)若是 n 小於 T->val,則去根結點的左子樹 (T->left ) 繼續查找。將 T->left 做爲 T,回到第1步。博客
算法實現以下:
typedef int ElementType; typedef struct BinaryTreeNode_ { ElementType val; struct BinaryTreeNode_ *left; struct BinaryTreeNode_ *right; }BinaryTreeNode; typedef BinaryTreeNode *BiTreeNode; // 查找一個結點 BiTreeNode FindNode(BiTreeNode BiTree,ElementType n) { if (BiTree == NULL) // 若是頭結點爲空,代表沒有找到元素 n return NULL; if (BiTree->val < n) // 結點值小於查找元素值,說明元素在結點的右邊 return FindNode(BiTree->right, n); // 遞歸,去右子樹繼續查找 else if (BiTree->val > n) return FindNode(BiTree->left, n); // 遞歸,去左子樹繼續查找 else return BiTree; }
該查找算法的時間複雜度爲 O(log2N)。
3、向 BST 中插入一個結點
向 BST 中插入一個結點 n 時,若 BST 中已經存在與該結點值相等的結點,則判斷結點已存在,不作任何操做;若是 BST 中不存在該結點,則將該結點將做爲一個新的葉子結點插入到 BST 中去(新結點老是 BST 的葉子結點),且須要保證插入後的樹是一個二叉查找樹。
能夠以相似查找算法的方式來遍歷二叉樹,從而找到插入的位置:
用算法來描述,向一個二叉查找樹 T(T 爲二叉樹的根結點)中插入一個結點,節點元素爲 n:
1)若 T 爲空,則將新結點插入到 T 所在的位置;
2)若 T 不爲空,且 T->val 小於 n,則代表應該把 n 插入到 T 的右子樹。將 T->right 做爲 T,回到第 1 步;
3)若 T 不爲空,且 T->val 大於 n,則代表應該把 n 插入到 T 的左子樹。將 T->left 做爲 T,回到第 1 步。
4)若 T->val 等於 n,則直接返回 T。
// 插入一個結點 BiTreeNode InsertNode(BiTreeNode BiTree, ElementType n) { if (BiTree == NULL) { // 樹爲空,或已到達葉子結點 // 爲新結點分配內存 BiTreeNode newNode = (BiTreeNode)malloc(sizeof(BinaryTreeNode)); if (newNode == NULL) { perror("malloc failed!\n"); exit(EXIT_FAILURE); } newNode->val = n; newNode->left = NULL; newNode->right = NULL; BiTree = newNode; } else if (BiTree->val < n) { // 插入值大於當前結點的值,則將元素插入到結點的右子樹 BiTree->right = InsertNode(BiTree->right, n); } else if (BiTree->val > n) { // 插入值小於當前結點的值,則將元素插入到結點的左子樹 BiTree->left = InsertNode(BiTree->left, n); } return BiTree; }
4、遍歷 BST
在上一篇博客中已經介紹了二叉樹的遍歷方法,主要有三種:先序遍歷、後序遍歷和中序遍歷。爲了更好的體現二叉查找樹的特性,咱們使用中序遍從來遍歷它:
// 中序遍歷 BST void TraverseTree(BiTreeNode BiTree) { if (BiTree != NULL) { TraverseTree(BiTree->left); cout << BiTree->val << endl; TraverseTree(BiTree->right); } }
5、刪除一個結點
刪除操做比較複雜,分爲三種狀況:
1)被刪除結點爲葉子結點:
能夠直接刪除該結點,如圖:
2)被刪除結點有一個左孩子或一個右孩子:
將孩子結點設爲該結點的父結點的孩子後,便可刪除該結點:
如圖,結點 2 是結點 5 的左孩子,他有一個右孩子 4。刪除結點 2 後,其右孩子 4 替代原來的結點 2 成爲結點 5 的左孩子。
如圖,結點 16 是結點 18 的左孩子,他有一個左孩子 15。刪除結點 16 後,其左孩子 15 替代原來的結點 16 成爲結點 18 的左孩子。
3)被刪除結點有兩個孩子結點:
這種狀況比較複雜,通常的刪除策略是:用被刪除結點的右子樹中的最小結點替代被刪除結點,並遞歸地刪除這個最小數據結點:
// 刪除一個結點 BiTreeNode DeleteNode(BiTreeNode BiTree, ElementType n) { BiTreeNode tmpNode = (BiTreeNode)malloc(sizeof(BinaryTreeNode)); if (tmpNode == NULL) { perror("malloc failed!\n"); exit(EXIT_FAILURE); } // 先找到要刪除的結點在二叉樹中的位置 if (BiTree == NULL) { // 沒有找到元素 perror("can`t find element\n"); return NULL; } if (BiTree->val < n) { // 元素值大於結點元素值,則去結點右子樹尋找 BiTree->right = DeleteNode(BiTree->right, n); return BiTree; } else if (BiTree->val > n) { // 元素值小於結點元素值,則去結點左子樹尋找 BiTree->left = DeleteNode(BiTree->left, n); return BiTree; } // 找到結點 else { tmpNode = BiTree; if (BiTree->right == NULL) { // 沒有右子樹,則直接返回左子樹 BiTree = BiTree->left; return BiTree; } else if (BiTree->left == NULL) { // 沒有左子樹,則直接返回右子樹 BiTree = BiTree->right; return BiTree; } // 有兩個孩子結點 tmpNode = FindMin(BiTree->right); // 尋找右子樹最小結點 tmpNode->right = DeleteMin(BiTree->right); // 刪除右子樹的最小結點 tmpNode->left = BiTree->left; // 左子樹保持不變 return tmpNode; } } // 查找最左葉子結點(最小的結點) BiTreeNode FindMin(BiTreeNode BiTree) { if (BiTree == NULL) return NULL; if (BiTree->left == NULL) // 結點沒有左子樹,即爲最左葉子結點 return BiTree; else // 有左子樹,則繼續在左子樹中查找 return FindMin(BiTree->left); } // 刪除最小結點 BiTreeNode DeleteMin(BiTreeNode BiTree) { if (BiTree->left == NULL) return BiTree->right; else { BiTree->left = DeleteMin(BiTree->left); return BiTree; } }
參考資料:
《數據結構與算法分析 C 語言描述》