二叉查找树BST

给定一个关键字 key 和 一个集合 Collection, 确定这个Collection是否包含给定的key,如果包含,找出该key对应的位置,这个过程为 查找 (Search)。

如果在 查找 过程中, Collection的结构始终不发生变化,那么称之为静态查找; 反之,需要频繁的向这个集合中添加、删除元素,从这样的集合中进行查找则称之为动态查找。

动态查找技术一般用树来存储查找集合,如二叉查找树。而二叉查找树的查找效率又与自身形态密切相关,需要对二叉查找树平衡化,保持其查找高效。本文就二叉查找树和平衡二叉树作简单介绍和分析以及代码实现,仅供学习参考。

1、二叉查找树

1.1  二叉查找树的定义

二叉查找树(Binary Search Tree, BST),也有称作二叉排序树,二叉检索树,二叉搜索树,检索树 等等。其数据结构表示和二叉树相同,在此基础之上还有其特殊的性质,递归定义为:

一颗非空的二叉查找树(Binary Search Tree, BST),满足如下特性:

  • 若根节点的左子树非空,则左子树所有节点数值小于根节点;
  • 若根节点的右子树非空,则右子树所有节点数值大于根节点;
  • 左右子树分别为一颗二叉查找树.
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. // define data type for the key  
  2. typedef int keyType;  
  3.   
  4. // define binary search tree data structure  
  5. struct BinaryTreeNode {  
  6.     keyType key;  
  7.     BinaryTreeNode* left; // left child  
  8.     BinaryTreeNode* right; // right child  
  9. };  
  10.   
  11. // alias for the tree  
  12. typedef BinaryTreeNode bstree;  
  13.   
  14. // alias for the tree node  
  15. typedef BinaryTreeNode bstnode;  

1.2 基本操作(查找、插入和删除节点)

(1) 查找key :从定义一颗查找二叉树来看,可以利用其特性根据关键字进行快速查找,这和顺序表的二分查找一样。

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. // search BST by the given key  
  2. bstnode* search_by_key(bstree* tree, keyType key) {  
  3.     bstnode* node = tree;  
  4.     int found = 0;  
  5.     while (NULL != node) {  
  6.         if (key == node->key) {  
  7.             found = 1;  
  8.             break;  
  9.         }  
  10.         node = (key > node->key) ? node->right : node->left;  
  11.     }  
  12.     return found ? node : NULL;  
  13. }  

(2) 插入key :向BST中插入一个key,不能破坏BST的规则

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. // insert a key to BST  
  2. int insert_key(bstree* &tree, keyType key) {  
  3.     if (NULL == tree) {  
  4.         tree = (bstnode*) malloc(sizeof(bstnode));  
  5.         tree->key = key;  
  6.         return 1;  
  7.     }  
  8.   
  9.     int found = 0;  
  10.     bstnode* curr = tree;  
  11.     bstnode* prev = NULL;  
  12.     while (NULL != curr) {  
  13.         // if already exists  
  14.         if (key == curr->key) {  
  15.             found = 1;  
  16.             break;  
  17.         }  
  18.         prev = curr;  
  19.         curr = (key > curr->key) ? curr->right : curr->left;  
  20.     }  
  21.     if (!found && NULL == curr) {  
  22.         curr = (bstnode*) malloc(sizeof(bstnode));  
  23.         curr->key = key;  
  24.         ((key > prev->key) ? prev->right : prev->left) = curr;  
  25.         return 1;  
  26.     }  
  27.     return 0;  
  28. }  

(3) 删除key :在BST中删除一个key,不能破坏BST的规则

删除操作需要注意:1. 若删除的元素是树根,则需要确保删除之后还能构建成新的树; 2. 按照查找二叉树的特性,按照中序遍历则必定有序,所以在删除某个节点之后可以寻找其中序前去或者后继来替代该节点。

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. // delete a key from the BST  
  2. int delete_key(bstree* &tree, keyType key) {  
  3.     // 设定临时树根,其key假设为无穷大,防止出现删除root的情况  
  4.     // 主要作用是让原来树的root可以像其他节点一样参与运算  
  5.     bstnode* head = (bstnode*) malloc(sizeof(bstnode));  
  6.     head->left = tree;  
  7.   
  8.     bstnode *curr = tree, *prev = head;  
  9.     bstnode *t1 = NULL, *t2 = NULL;  
  10.     int found = 0;  
  11.     while (NULL != curr) {  
  12.         if (key == curr->key) {  
  13.             found = 1;  
  14.             break;  
  15.         }  
  16.         prev = curr;  
  17.         curr = ((key > curr->key) ? curr->right : curr->left);  
  18.     }  
  19.   
  20.     if (found) {  
  21.         // delete the node with the given key  
  22.         if (NULL == curr->left) {     // when the left child of the node is NULL  
  23.             ((curr == prev->left) ? prev->left : prev->right) = curr->right;  
  24.             free(curr);  
  25.         } else if (NULL == curr->right) { // when the right child of the node is NULL  
  26.             ((curr == prev->left) ? prev->left : prev->right) = curr->left;  
  27.             free(curr);  
  28.         } else {     // when the node has two children  
  29.             // 按照二叉查找树的特性,保持中序遍历顺序即可  
  30.             // 即用该节点的中序前序后者后继替代该节点即可  
  31.             t1 = curr->left;  
  32.             while (NULL != t1->right) {  
  33.                 t2 = t1;  
  34.                 t1 = t1->right;  
  35.             }  
  36.             curr->key = t1->key;  
  37.             ((NULL == t2) ? curr->left : t2->right) = t1->left;  
  38.             free(t1);  
  39.         }  
  40.     }  
  41.   
  42.     // 还原树根  
  43.     tree = head->left;  
  44.     free(head);  
  45.   
  46.     return found;  
  47. }  

测试 操作

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. int main() {  
  2.   
  3.     cout << "插入构建二叉查找树:4 2 5 3 1 6" << endl;  
  4.     bstree * bst = NULL;  
  5.     insert_key(bst, 4);  
  6.     insert_key(bst, 2);  
  7.     insert_key(bst, 5);  
  8.     insert_key(bst, 3);  
  9.     insert_key(bst, 1);  
  10.     insert_key(bst, 6);  
  11.     cout << endl;  
  12.     cout << "遍历二叉查找树 :" << endl << "前序\t : ";  
  13.     rpre_order(bst);  
  14.     cout << endl << "中序\t : ";  
  15.     rin_order(bst);  
  16.     cout << endl;  
  17.     cout << endl;  
  18.   
  19.     cout << "删除元素 4 : ";  
  20.     int r = delete_key(bst, 4);  
  21.     if (r) {  
  22.         cout << "Success";  
  23.     } else {  
  24.         cout << "Failed";  
  25.     }  
  26.     cout << endl;  
  27.     cout << "删除元素 32 : ";  
  28.     r = delete_key(bst, 32);  
  29.     if (r) {  
  30.         cout << "Success";  
  31.     } else {  
  32.         cout << "Failed";  
  33.     }  
  34.     cout << endl;  
  35.   
  36.     cout << endl;  
  37.     cout << "刪除之后 :" << endl << "前序\t : ";  
  38.     rpre_order(bst);  
  39.     cout << endl << "中序\t : ";  
  40.     rin_order(bst);  
  41.     cout << endl;  
  42.   
  43.     //  
  44.     destory_btree(bst);  
  45.   
  46.     return 0;  
  47. }  

测试结果:

[plain]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. 插入构建二叉查找树:4 2 5 3 1 6  
  2.   
  3. 遍历二叉查找树 :  
  4. 前序   : 4  2  1  3  5  6    
  5. 中序   : 1  2  3  4  5  6    
  6.   
  7. 删除元素 4 : Success  
  8. 删除元素 32 : Failed  
  9.   
  10. 刪除之后 :  
  11. 前序   : 3  2  1  5  6    
  12. 中序   : 1  2  3  5  6    

1.3 小结

对于一颗二叉查找树来说,如果分布较为对称,每次比较都会将查找分为缩减一半,其时间复杂度为 Log2(N), 最坏的情况下,即这个查找树二叉树退化成只有左子树或者右子树的情况下,其复杂度为N .

这样看来,二叉查找树的查找效率和它自身的形态有密切的关系,一颗形态分布匀称的查找二叉树才能发挥出它的真正的效率。


在经过多次插入、删除操作之后,如果不加控制,一颗二叉查找树很有可能退化,这样就大大降低了其查找效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值