数据结构基础(17) --二叉查找树的设计与实现

二叉排序树的特征

二叉排序树或者是一棵空树,或者是具有如下特性的二叉树:

    1.每一元素都有一个键值, 而且不允许重复;

    2.若它的左子树不空,则左子树上所有结点的值均小于根结点的值;

    3.若它的右子树不空,则右子树上所有结点的值均大于根结点的值;

    4.它的左、右子树也都分别是二叉排序树。



二叉排序树保存的元素构造

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. template <typename Type>  
  2. class Element  
  3. {  
  4. public:  
  5.     Element(const Type& _key): key(_key) {}  
  6.     Element():key(0) {}  
  7.     Type key;  
  8.     //在这儿可以很容易的添加更多的数据  
  9.     //方便对Element进行扩展  
  10. };  

二叉排序树节点的设计与实现

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. template <typename Type>  
  2. class BstNode  
  3. {  
  4.     friend class BsTree<Type>;  
  5.   
  6. public:  
  7.     BstNode(const Element<Type> &_data = 0,  
  8.             BstNode *_leftChild = NULL,  
  9.             BstNode *_rightChild = NULL)  
  10.         : data(_data), leftChild(_leftChild), rightChild(_rightChild) {}  
  11.   
  12.     const Type &getData() const  
  13.     {  
  14.         return data.key;  
  15.     }  
  16.   
  17. private:  
  18.     //Node当中保存的是Element元素  
  19.     Element<Type> data;  
  20.     BstNode *leftChild;  
  21.     BstNode *rightChild;  
  22.   
  23.     void display(int i);  
  24. };  
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //中序遍历二叉树:  
  2. //能够保证该二叉树元素按照递增顺序打印出来  
  3. template <typename Type>  
  4. void BstNode<Type>::display(int i)  
  5. {  
  6.     //首先访问左子树  
  7.     if (leftChild != NULL)  
  8.         leftChild->display(2*i);  
  9.   
  10.     //访问中间节点  
  11.     //Number表示为如果该树为完全二叉树/满二叉树, 其编号为几  
  12.     std::cout << "Number: " << i << ", data.key = " << data.key << std::endl;  
  13.   
  14.     //访问右子树  
  15.     if (rightChild != NULL)  
  16.         rightChild->display(2*i+1);  
  17. }  

二叉排序树的构造

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. template <typename Type>  
  2. class BsTree  
  3. {  
  4. public:  
  5. //构造与析构  
  6.     BsTree(BstNode<Type> *init = NULL): root(init) {}  
  7.     ~BsTree()  
  8.     {  
  9.         if (!isEmpty())  
  10.             makeEmpty(root);  
  11.     }  
  12.   
  13. //二叉查找树的三大主力:插入, 删除, 搜索(又加入了一个迭代搜索)  
  14.     //插入  
  15.     bool insert(const Element<Type> &item);  
  16.     //删除  
  17.     void remove(const Element<Type> &item)  
  18.     {  
  19.         remove(item, root);  
  20.     }  
  21.     //递归搜索  
  22.     const BstNode<Type>* search(const Element<Type> &item)  
  23.     {  
  24.         return search(item, root);  
  25.     }  
  26.     //迭代搜索  
  27.     const BstNode<Type> *searchByIter(const Element<Type> &item);  
  28.   
  29. //实用函数  
  30.     void display() const  
  31.     {  
  32.         if (root != NULL)  
  33.             root->display(1);  
  34.     }  
  35.     void visit(BstNode<Type> * currentNode) const  
  36.     {  
  37.         std::cout << "data.key = "  
  38.                   << currentNode->data.key << std::endl;  
  39.     }  
  40.     bool isEmpty() const  
  41.     {  
  42.         return root == NULL;  
  43.     }  
  44.     void makeEmpty(BstNode<Type> *subTree);  
  45.     //中序遍历  
  46.     void levelOrder() const;  
  47.   
  48. private:  
  49.     const BstNode<Type>* search(const Element<Type> &item,  
  50.                                 const BstNode<Type> *currentNode);  
  51.     void remove(const Element<Type> &item,  
  52.                 BstNode<Type> *¤tNode);  
  53.   
  54. private:  
  55.     BstNode<Type> *root;  
  56. };  

二叉排序树的插入算法

    根据动态查找表的定义,插入操作在查找不成功时才进行;若二叉排序树为空树,则新插入的结点为新的根结点;否则,新插入的结点必为一个新的叶子结点,其插入位置由查找过程得到。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //二叉排序树插入的实现与解析  
  2. template <typename Type>  
  3. bool BsTree<Type>::insert(const Element<Type> &item)  
  4. {  
  5.     //如果这是新插入的第一个节点  
  6.     if (root == NULL)  
  7.     {  
  8.         root = new BstNode<Type>(item);  
  9.         root->leftChild = root->rightChild = NULL;  
  10.         return true;  
  11.     }  
  12.   
  13.     BstNode<Type> *parentNode = NULL;   //需要插入位置的父节点  
  14.     BstNode<Type> *currentNode = root;  //需要插入的位置  
  15.     while (currentNode != NULL)  
  16.     {  
  17.         //如果二叉树中已经含有了该元素, 则返回插入出错  
  18.         if (item.key == currentNode->data.key)  
  19.             return false;  
  20.   
  21.         parentNode = currentNode;  
  22.         //如果要插入的元素大于当前指向的元素  
  23.         if (item.key < currentNode->data.key)  
  24.             currentNode = currentNode->leftChild;   //向左搜索  
  25.         else  
  26.             currentNode = currentNode->rightChild;  //向右搜索  
  27.     }  
  28.   
  29.     //此时已经查找到了一个比较合适的插入位置了  
  30.     if (item.key < parentNode->data.key)  
  31.         parentNode->leftChild = new BstNode<Type>(item);  
  32.     else  
  33.         parentNode->rightChild = new BstNode<Type>(item);  
  34.   
  35.     return true;  
  36. }  

二叉排序树的查找算法

若二叉排序树为空,则查找不成功;否则:

    1.若给定值等于根结点的关键字,则查找成功;

    2.若给定值小于根结点的关键字,则继续在左子树上进行查找;

    3.若给定值大于根结点的关键字,则继续在右子树上进行查找。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //二叉排序树搜索的设计与实现  
  2. //递归搜索  
  3. template <typename Type>  
  4. const BstNode<Type>* BsTree<Type>::search(const Element<Type> &item,  
  5.         const BstNode<Type> *currentNode)  
  6. {  
  7.     if (currentNode == NULL)  
  8.         return NULL;  
  9.     if (currentNode->data.key == item.key)  
  10.         return currentNode;  
  11.   
  12.     if (item.key < currentNode->data.key)  
  13.         return search(item, currentNode->leftChild);  
  14.     else  
  15.         return search(item, currentNode->rightChild);  
  16. }  
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //迭代搜索  
  2. template <typename Type>  
  3. const BstNode<Type> *BsTree<Type>::searchByIter(const Element<Type> &item)  
  4. {  
  5.     for (BstNode<Type> *searchNode = root;  
  6.             searchNode != NULL;  
  7.             /*empty*/)  
  8.     {  
  9.         if (item.key == searchNode->data.key)  
  10.             return searchNode;  
  11.   
  12.         if (item.key < searchNode->data.key)  
  13.             searchNode = searchNode->leftChild;  
  14.         else  
  15.             searchNode = searchNode->rightChild;  
  16.     }  
  17.   
  18.     return NULL;  
  19. }  

二叉排序树的删除算法

    和插入相反,删除在查找成功之后进行,并且要求在删除二叉排序树上某个结点之后,仍然保持二叉排序树的特性

 

删除分三种情况:

    1.被删除的结点是叶子节点:其双亲结点中相应指针域的值改为“空”, 并将该节点删除;

    2.被删除的结点只有左子树或者只有右子树:其双亲结点的相应指针域的值改为 “指向被删除结点的左子树或右子树”, 然后删除该节点;

    3.被删除的结点既有左子树,也有右子树:以其前驱替代之,然后再删除该前驱结点;

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //二叉排序树节点删除的实现与解析如下  
  2. template <typename Type>  
  3. void BsTree<Type>::remove(const Element<Type> &item,  
  4.                           BstNode<Type> *¤tNode)  
  5. {  
  6.     if (currentNode != NULL)  
  7.     {  
  8.         //如果要删除的元素小于当前元素  
  9.         if (item.key < currentNode->data.key)  
  10.             remove(item, currentNode->leftChild);   //向左搜索删除  
  11.         //如果要删除的元素大于当前元素  
  12.         else if (item.key > currentNode->data.key)  
  13.             remove(item, currentNode->rightChild);  //向右搜索删除  
  14.         //如果要删除掉的元素等于当前元素(找到要删除的元素了)  
  15.         // 并且当前节点的左右子女节点都不为空  
  16.         else if ((currentNode->leftChild != NULL) && (currentNode->rightChild != NULL))  
  17.         {  
  18.             //从当前节点的右子女节点开始,  
  19.             //不断向左寻找, 找到从当前节点开始中序遍历的第一个节点  
  20.             //找到的这一个节点是在当前子树中, 大于要删除的节点的第一个节点  
  21.             BstNode<Type> *tmp = currentNode->rightChild;  
  22.             while (tmp->leftChild != NULL)  
  23.                 tmp = tmp->leftChild;  
  24.   
  25.             //用搜索到的节点值覆盖要删除的节点值  
  26.             currentNode->data.key = tmp->data.key;  
  27.             //删除搜索到的节点  
  28.             remove(currentNode->data, currentNode->rightChild);  
  29.         }  
  30.         //如果当前节点就是要删除的节点  
  31.         //并且其左子女(和/或)右子女为空  
  32.         //默认包含了左右子女同时为空的情况:  
  33.         //即: 在if中肯定为true  
  34.         else  
  35.         {  
  36.             BstNode<Type> *tmp = currentNode;  
  37.             //如果左子女为空  
  38.             if (currentNode->leftChild == NULL)  
  39.                 //则用他的右子女节点顶替他的位置  
  40.                 currentNode = currentNode->rightChild;  
  41.             //如果右子女为空  
  42.             else  
  43.                 //则用他的左子女节点顶替他的位置  
  44.                 currentNode = currentNode->leftChild;  
  45.             //释放节点  
  46.             delete tmp;  
  47.         }  
  48.     }  
  49. }  

二叉查找树的几个实用操作

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //清空二叉树  
  2. template <typename Type>  
  3. void BsTree<Type>::makeEmpty(BstNode<Type> *subTree)  
  4. {  
  5.     if (subTree != NULL)  
  6.     {  
  7.         if (subTree->leftChild != NULL)  
  8.             makeEmpty(subTree->leftChild);  
  9.         if (subTree->rightChild != NULL)  
  10.             makeEmpty(subTree->rightChild);  
  11.   
  12.         delete subTree;  
  13.     }  
  14. }  
[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. //二叉查找树的层次遍历  
  2. template <typename Type>  
  3. void BsTree<Type>::levelOrder() const  
  4. {  
  5.     std::queue< BstNode<Type> * > queue;  
  6.     queue.push(root);  
  7.   
  8.     while (!queue.empty())  
  9.     {  
  10.         BstNode<Type> *currentNode = queue.front();  
  11.         queue.pop();  
  12.   
  13.         visit(currentNode);  
  14.         if (currentNode->leftChild != NULL)  
  15.             queue.push(currentNode->leftChild);  
  16.         if (currentNode->rightChild != NULL)  
  17.             queue.push(currentNode->rightChild);  
  18.     }  
  19. }  

二叉排序树的性能分析

     对于每一棵特定的二叉排序树,均可按照平均查找长度的定义来求它的 ASL 值,显然,由值相同的 n 个关键字,构造所得的不同形态的各棵二叉排序树的平均查找长度的值不同,甚至可能差别很大(如果二叉查找树退化成一条链表, 则其插入/删除/查找的性能都会退化为O(N))。

     但是在随机情况下, 二叉排序树的搜索, 插入, 删除操作的平均时间代价为O(logN);


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二叉排序树的二叉链表存储结构的类型定义如下: typedef struct node{ int data; //用整数表示一个结点的名 struct node *LChild,*RChild; //左右指针域 }BSTNode,*BSTree; 设计算法并编写程序求解以下几个问题。 8 12 14 10 7 3 15 6 2 4 1 5 11 9 13 16 13 (1)键盘输入一个元素序列创建一棵二叉排序树,输出该二叉排序树的中序遍历序列; 例如,若输入 45,24,55,12,37,53,60,23,40,70 则创建的二叉排序树为: 输出结果为:12 23 24 37 40 45 53 55 60 70 (2)在(1)中所得的二叉排序树中插入一个值为 58 的结点,再输出它的中序遍历序列,输出 结果为:12 23 24 37 40 45 53 55 58 60 70 (3)在(1)中所得的二叉排序树中删除值为 45 的结点,再输出它的中序遍历序列,输出结果 为:12 23 24 37 40 53 55 58 60 70 (4)利用(1)中所得的二叉排序树的所有叶子结点构造一个带头结点的单链表 L。要求不能 破坏这棵二叉排序树。所得的单链表 L 如下。 输出该链表各结点的值,输出结果为:23 40 53 70 (5)设计算法将(1)中所得的二叉排序树的左右子树进行交换,由于二叉树是一种递归定义, 所以子树的左右两棵子树也要相交换,依此类推。最后输出所得到的二叉树的中序遍历序列。 例如,经过上述操作后,(1)中所得的二叉排序树变为如下形式。 输出该二叉树的中序序列,结果为:70 60 55 53 45 40 37 24 23 12 (6)设计算法统计并输出(1)中所得的二叉排序树中只有一个孩子结点结点个数。输出结 果为:3(7)在(1)中所得的二叉排序树中,设计算法并编写程序输出结点 40 的所有祖先结点。输 出结果为:45 24 37

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值