当前位置:   article > 正文

二叉搜索树代码实现_二叉查找树代码实现

二叉查找树代码实现

目录​​​​​​​

一、概念:

二、代码实现二叉搜索树

1.  二叉搜索树的查找

 2. 二叉搜索树类的实现

3.  二叉搜索树的插入

4.  二叉搜索树的删除


一、概念:

        二叉搜索树:二叉搜索树又称二叉排序树,其具有以下特点:

1.可能为空树

2.其左子树的节点值均小于根节点的值

3.其右子树的节点值均大于根节点的值

4.其左右子树均构成二叉排序树

示例:

二、代码实现二叉搜索树

1.  二叉搜索树的查找

        假设我们要查找的值为6,假设要查找的值为key,由二叉搜索树的特点可知

        a. 从根节点开始进行查找,如果要查询的key值小于根节点,就进入左子树进行查找,如果要查询的key值大于根节点,就进入右子树进行查找。

        b.最多查找的次数为二叉搜索树的高度次,如果走到空的位置,则未查到,这个值不存在。

根据以上描述,我们根据上图对查找的过程如下:

        我们可以看出当key=6时,此时从根节点开始,key小于根节点的值,因此,要从根节点的左子树进行查找,此时到节点3处,可以得知key的值大于3,因此要在节点3的右子树进行查找。此时找到等于key的节点。

        上述过程用代码进行描述:

首先定义一个二叉搜索树节点的结构体:

  1. template<class K>
  2. struct BSTreeNode
  3. {
  4. typedef BSTreeNode<K> Node;
  5. BSTreeNode(const K& val)
  6. :_left(nullptr)
  7. ,_right(nullptr)
  8. ,_val(val)
  9. {}
  10. Node* _left;
  11. Node* _right;
  12. K _val;
  13. };

 2. 二叉搜索树类的实现

  1. template<class K>
  2. class BSTree {
  3. public:
  4. typedef BSTreeNode<K> Node;
  5. private:
  6. Node* _root = nullptr; //将根节点初始化为空
  7. };

        接下来根据上述描述实现查找功能函数:

  1. bool Find(const K& val) // 传入要查找的key的值 val
  2. {
  3. Node* cur = _root; //定义一个当前二叉搜索树的指针指向root
  4. if (cur == nullptr) //如果当前指向root节点的指针为nullptr,表示当前树为空 返回false
  5. {
  6. return false;
  7. }
  8. while (cur) //对树进行查找,如果为空,则表示未找到,退出循环
  9. {
  10. if (cur->_val < val) //如果当前节点的值小于要查询的key值
  11. {
  12. cur = cur->_right; //向当前节点的右子树进行查找
  13. }
  14. else if (cur->_val > val) //如果当前节点的值大于要查询的key值
  15. {
  16. cur = cur->_left; //向当前节点的左子树进行查找
  17. }
  18. else
  19. {
  20. return true; //此时找到相等的节点
  21. }
  22. }
  23. return false; //未找到返回false
  24. }

3.  二叉搜索树的插入

        a. 当前树如果为空树,即根节点为空(root==nullptr),则申请一个新的节点,并赋值给根节点

        b.如果树不为空,则根据二叉搜索树的性质找到合适的插入位置,插入新的节点。

假设我们要插入的节点为15,那么根据图我们可以表示为:

根据图示可以看出,如果要插入15这个节点,首先从根节点进行查找其合适的插入位置,根据左子树均小于根节点以及右子树大于根节点的性质,可以找到15的插入位置为节点14的右子树。

据图所示,当遍历二叉搜索树找合适的插入位置过程当中,结束条件为找到节点为空的位置,而当插入新节点的过程中,需要找到空节点的上一个位置链接新的节点,因此我们需要保存相关的节点。

代码实现如下:

  1. bool insert(const K& val) //传入要插入的节点值
  2. {
  3. if (_root == nullptr) //如果根节点为空,即空树,那么申请一个新的节点,并返回true
  4. {
  5. _root = new Node(val);
  6. return true;
  7. }
  8. Node* cur = _root; //定义一个节点cur指向根节点
  9. Node* parent = nullptr; //保存父节点,链接新的节点
  10. while (cur) //如果cur为空,则代表找到了插入的位置
  11. {
  12. if (cur->_val < val) //如果当前节点的值小于要查询的key值
  13. {
  14. parent = cur; //更新父节点为当前节点的值
  15. cur = cur->_right; //向右查找插入位置
  16. }
  17. else if (cur->_val > val)//如果当前节点的值大于要查询的key值
  18. {
  19. parent = cur; //更新父节点为当前节点的值
  20. cur = cur->_left; //向左查找插入位置
  21. }
  22. else
  23. {
  24. return false; //如果找到了与要插入值相同的节点,那么就无法插入,返回false
  25. }
  26. }
  27. cur = new Node(val); //这里已经找到了合适的位置,此刻申请一个新的节点赋值给cur
  28. if (parent->_val < val) //此刻parent指向要插入的节点的父节点位置
  29. {
  30. parent->_right = cur; //如果父节点的值小于要插入的值,则将其链接到右子树
  31. }
  32. else
  33. {
  34. parent->_left = cur; //如果父节点的值小于要插入的值,则将其链接到左子树
  35. }
  36. return true; //插入成功
  37. }

4.  二叉搜索树的删除

a. 首先查找当前二叉搜索树中是否存在要删除的节点,如果不存在则返回

b. 根据二叉搜索树的性质可知:二叉搜索树的左子树和右子树分别都是二叉搜索树,所以在删除节点的过程中应当始终保证二叉搜索树这一性质,因此删除节点分为以下几种情况:

(1) 删除叶子节点(即无孩子节点):直接进行删除,删除叶子节点并不会改变二叉搜索树其他节点的位置。

        如图所示,如果要删除的节点为叶子节点时,只需找到其所在位置对其进行删除即可,二叉搜索树其他节点依然构成二叉搜索树。只需将删除位置节点的父节点的指向置空即可。

(2)要删除的节点只有一个孩子节点:

        此处我们以14这个节点为例,其只有13这一个左孩子节点。由图可知,如果我们将14这个节点删除,那么我们需要将其左孩子节点13与其父节点10链接在一起,根据搜索树的性质,我们可以很清楚的看出应当将13链接到10的右孩子处。如下图所示:

        接下来我们看下一种情况,如图所示,如果我们此刻要删除的是3这个节点,那么就要将3的孩子节点连接到节点3的父节点,那么根据搜索树性质可知,左子树小于根节点,右子树大于根节点。那么节点3的右孩子节点6需要链接到父节点8的左孩子处。

因此会得到如下情况:

由以上两张图我们可以看出,同样都是删除只有一个孩子的节点,其操作是相同的。

只需将要删除节点的孩子节点链接至要删除节点的父节点处即可。

但是父节点在链接节点的时候,应该链接到左边还是右边呢?

        根据上图我们可以看出,如果我们要删除的节点是父节点的右节点,那么就将要删除节点的孩子连接到父节点的右孩子处,相反,如果我们要删除的节点是父节点的左孩子,那么就将要删除节点的孩子连接到父节点的左孩子处。

同时我们也可以看到,我们可以将(1)和(2)归类为同一种情况:

         在(1)中删除叶子节点的情况,最终都是将要删除节点的父节点连接至空,而(2)中情况是将要删除节点的父节点连接至其左孩子或右孩子处,此时(1)中的情况便会成为一种特殊情况。

(3)要删除的节点有两个孩子节点:

        如果要删除的节点既有左孩子又有右孩子,那么就无法使用上述的方式来进行删除,因为要始终保持二叉搜索树的左右子树均为二叉搜索树,即左子树均小于根节点,右子树均大于根节点。

        因此,我们可以采取如下方式进行删除此类节点,假设我们要删除的节点为3这个节点,其既有左孩子又有右孩子:

我们可以找到其右子树中最左侧的值,即其右子树中的最小值与其进行交换值:

此时我们只需删除4这个节点即可。方法如下:

a. 首先找到右子树最小的节点,我们定义为rightMin

b. 此时我们只需删除右子树中最小值这个节点即可

c. 我们所找到的是右子树中的最小节点,根据性质我们可以知道最小值节点应是右子树中最左的节点,因此最小值节点不会存在左孩子,但是有可能存在右孩子。

d.根据c描述,我们可以知道删除这个节点的时候遵循的内容是(2)中所述删除只有一个孩子节点时的情形,因此不做过多描述。

最终删除代码实现如下:

  1. bool erase(const K& val)
  2. {
  3. if (_root == nullptr)
  4. {
  5. return false;
  6. }
  7. Node* cur = _root; //当前节点指向根节点
  8. Node* parent = nullptr; //定义一个父节点
  9. while (cur) //首先是查找要删除的节点,如果未找到则返回false
  10. {
  11. if (cur->_val < val) //如果当前节点的值小于要删除的结点的值
  12. {
  13. parent = cur; //保留父节点的位置
  14. cur = cur->_right; //向右子树进行查找
  15. }
  16. else if (cur->_val > val)//如果当前节点的值大于要删除的结点的值
  17. {
  18. parent = cur; //保留父节点的位置
  19. cur = cur->_left; //向左子树进行查找
  20. }
  21. else { //此时代表找到了要删除的节点
  22. if (cur->_right == nullptr) //如果当前节点的右孩子为空,则表示此时是要删除的节点只有一个孩子或者无孩子
  23. {
  24. if (cur == _root) //如果此时节点为根节点
  25. {
  26. _root = cur->_left; //要将根节点更新至左孩子处
  27. }
  28. else
  29. {
  30. if (cur == parent->_right) //如果当前要删除节点为父节点的右孩子
  31. {
  32. parent->_right = cur->_left; //将要删除节点的孩子链接至父节点的右孩子处
  33. }
  34. else { //如果当前要删除节点为父节点的左孩子
  35. parent->_left = cur->_left; //将要删除节点的孩子链接至父节点的左孩子处
  36. }
  37. }
  38. delete cur; //删除节点
  39. return true;
  40. }
  41. else if (cur->_left == nullptr) //此处与上述思路相同,逻辑相反
  42. {
  43. if (cur == _root)
  44. {
  45. cur = cur->_right;
  46. }
  47. else
  48. {
  49. if (cur == parent->_right)
  50. {
  51. parent->_right = cur->_right;
  52. }
  53. else
  54. {
  55. parent->_left = cur->_right;
  56. }
  57. }
  58. delete cur;
  59. return true;
  60. }
  61. else // 此处代表要删除的节点不仅有左孩子也有右孩子,无法直接删除
  62. {
  63. Node* rightMin = cur->_right; //定义一个右子树最小节点的指针
  64. Node* parent = _root; //定义父节点
  65. while (rightMin->_left) //查找右子树中值最小的节点
  66. {
  67. parent = rightMin; //更新父节点
  68. rightMin = rightMin->_left;
  69. }
  70. cur->_val = rightMin->_val; //将右子树中最小节点的值赋值给要删除的节点
  71. if (rightMin == parent->_left) // 此时只需删除右子树中最小值的节点即可,此时要删除的节点变为cur
  72. parent->_left = rightMin->_right;// 如果当前节点的值是父节点的左孩子,将当前节点cur的右孩子链接至父节点的左孩子
  73. else
  74. parent->_right = rightMin->_right;//如果当前节点的值是父节点的右孩子,将当前节点cur的右孩子链接至父节点右孩子
  75. delete rightMin; //删除节点
  76. return true;
  77. }
  78. }
  79. }
  80. return false;
  81. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/910081
推荐阅读
相关标签
  

闽ICP备14008679号