• 搜索二叉树实现(非递归版本)


    目录

    一,搜索二叉树是个啥?

    二,搜索二叉树的实现

    1.前期工作

    2.方法实现

    1.插入

    2,查找

    3.删除

    三,实现二叉搜索树的全部代码


    一,搜索二叉树是个啥?

    话不多所,先给各位来一个搜索二叉树:

    从这棵树中可以看到这棵树有如下性质:

    1.根节点的左节点的值小于根节点的值,根节点的右节点的值大于根节点的值。

    2.这棵树的中序遍历的结果是一个升序的数组。

    3.这棵树的左子树和右子树都是一颗搜索二叉树。

    以上三点便是一棵搜索二叉树的性质!!!

    二,搜索二叉树的实现

    1.前期工作

         要实现一棵搜索二叉树,首先便要实现它的各个节点。实现如下:

    1. template<class K>
    2. struct BSNode
    3. {
    4. BSNode(const K& key)
    5. :_left(nullptr)
    6. ,_right(nullptr)
    7. ,_key(key)
    8. {}
    9. BSNode* _left;
    10. BSNode* _right;
    11. K _key;
    12. };

    这个节点的成员就是它的左指针_left,右指针_right,还有这个节点里包含的一个值_key。

          接下来便要开始实现一下这棵树。实现如下:

    1. template<class K>
    2. class BSTree
    3. {
    4. public:
    5. private:
    6. BSNode* _root;
    7. }

    这棵树的成员便只有一个,那便是_root这个根节点。

    2.方法实现

    1.插入

    在前期工作准备好以后便要来实现我们的方法了,现在来实现我们的插入方法。实现思路如下:

    1.因为我们的_root是是私有的,所以我们不能实现一个需要传参的Insert方法。所以它必须是无参的。

    2.要实现一个无参的方法,那我们就得套一层_Insert()方法在Insert()方法里面。

    3.实现_Insert()方法步骤如下:

    1.如果_root是nullptr便new一个节点,让_root接收这个新节点。

    2.如果key比我当前的节点值要大,便向右走。

    3.如果比我当前的节点值要小,那就向左走。

    4.如果走到空(_root的替代值未为nullptr)那就在该位置生成一个新节点,并让该节点的parent的左或者右指针指向这个新节点。

    代码如下:

    实现无参:

    1. bool Insert( const K& key)
    2. {
    3. return _Insert(key);
    4. }

    _Insert()方法实现:

    1. bool _Insert(const K& key)
    2. {
    3. if (_root == nullptr)//若_root是一个nullptr那就给_root new 一个节点
    4. {
    5. _root = new BNode(key);
    6. return true;
    7. }
    8. else
    9. {
    10. BNode* cur = _root;
    11. BNode* parent = nullptr;
    12. while (cur!=nullptr)//找位置
    13. {
    14. parent = cur;
    15. if (cur->_key < key)
    16. {
    17. cur = cur->_right;
    18. }
    19. else if (cur->_key > key)
    20. {
    21. cur = cur->_left;
    22. }
    23. else
    24. {
    25. return false;
    26. }
    27. }
    28. cur = new BNode(key);//找到后便给这个位置new一个节点
    29. if (key > parent->_key)//判断一下是左边还是右边然后链接
    30. {
    31. parent->_right = cur;
    32. }
    33. else
    34. {
    35. parent->_left = cur;
    36. }
    37. return true;
    38. }
    39. }
    2,查找

       查找方法的实现也很简单,其实就是将_Insert()里面的查找代码给复制一份过来便可以了。同样的,我们的查找算法在类的外边也是不能调用_root的,所以也会有封装。Find()函数实现如下:

    1. bool Find(const K& key)
    2. {
    3. return _Find(key);
    4. }

         我们的_Find()函数实现如下:

    1. bool _Find(const K& key)
    2. {
    3. BNode* cur = _root;
    4. while (cur)
    5. {
    6. if (key > cur->_key)
    7. {
    8. cur = cur->_right;
    9. }
    10. else if (key < cur->_key)
    11. {
    12. cur = cur->_left;
    13. }
    14. else
    15. {
    16. return true;
    17. }
    18. }
    19. return false;
    20. }
    3.删除

    删除方法的实现大概是最难写的一个代码了,首先我们得先找到这个要删除的节点找到以后分为三种情况讨论:

    以如下搜索二叉树为例:

    1.要删除节点的左节点为空。如以下情况:

    比如要删除10这个节点,我们该如何操作呢?

    我们的操作如下:

    1.找到我的父亲。

    2.判断我是父亲的那个节点。

    3.如果我是父亲的左节点便让父亲的左节点连接到我的右节点上。如果我是父亲右节点,便让父亲的右节点指向我的右节点。

    但是这里需要注意一个点:如果我是root,我便没有父亲。如以下情况

    在这种情况下我们便可以直接让右节点担任root节点:

    1. if (cur == _root)
    2. {
    3. _root = _root->_right;
    4. }

    左节点为空的情况下实现删除代码如下:
     

    1. if (cur->_left == nullptr)
    2. {
    3. if (cur == _root)
    4. {
    5. _root = _root->_right;
    6. }
    7. else
    8. {
    9. if (cur->_key > parent->_key)
    10. {
    11. parent->_right = cur->_right;
    12. }
    13. else
    14. {
    15. parent->_left = cur->_right;
    16. }
    17. }
    18. return true;
    19. }

    2.要删除的节点的右节点为空。
    其实这种情况下的的代码的值和前面的实现逻辑是一样的,

    所以不解释直接给出实现代码:

    1. else if (cur->_right == nullptr)
    2. {
    3. if (cur == _root)
    4. {
    5. _root = cur->_left;
    6. }
    7. else
    8. {
    9. if (cur->_key > parent->_key)
    10. {
    11. parent->_right = cur->_left;
    12. }
    13. else
    14. {
    15. parent->_left = cur->_left;
    16. }
    17. }
    18. return true;
    19. }

    3.当我要删除的节点的左右两个节点都在

    这个删除便是我们这个删除方法里面最难实现的一个代码,在这里我们要使用替换法来删除:

    1.如何使用替换法呢?

    步骤:1.定义parent和subLeft,subLeft定义为cur->right。

               记录我的父亲,这个父亲要初始化cur(当前节点)。

                2.找到当前节点的右子树的最左节点subLeft并更新parent位subLeft的父亲节点。

                3.交换cur节点和subLeft两个节点的值(使用swap).

                4.链接。

     了解完以上步骤以后写下如下代码:

    1. else
    2. {//有两个孩子,替换法。(找右子树的最左节点)
    3. BNode* Parent = cur;
    4. BNode* SubLeft = cur->_right;
    5. while (SubLeft->_left)
    6. {
    7. Parent = SubLeft;
    8. SubLeft = SubLeft->_left;
    9. }
    10. swap(cur->_key, SubLeft->_key);
    11. if (SubLeft == Parent->_left)
    12. {
    13. Parent->_left = SubLeft->_left;
    14. }
    15. else
    16. {
    17. Parent->_right = SubLeft->_left;
    18. }
    19. return true;
    20. }

     在这里解释一下:

    1.为什么parent要初始为cur,如以下例子:

                   

    假如是以上的情况,那我的这段代码是不会进去的:

    1. while (SubLeft->_left)
    2. {
    3. Parent = SubLeft;
    4. SubLeft = SubLeft->_left;
    5. }

    那如果我的parent 如果赋值为nullptr的话,那便会解引用nullptr:

    1. if (SubLeft == Parent->_left)
    2. {
    3. Parent->_left = SubLeft->_left;
    4. }
    5. else
    6. {
    7. Parent->_right = SubLeft->_left;
    8. }

    所以我们必须要将parent初始化为cur。这个时候也能删除。

    2.链接该如何连接?

    我实现的连接代码是这样的:

    1. if (SubLeft == Parent->_left)
    2. {
    3. Parent->_left = SubLeft->_left;
    4. }
    5. else
    6. {
    7. Parent->_right = SubLeft->_left;
    8. }

    在这里我们首先得先判断一下我们的subLeft是我的parent节点的哪一位?

    可能是右节点:

    就像我们上面的删除8的情况一样,我要删除的是根节点,我的根节点的左节点是nullptr。

    也可能是左节点:

    我进入了循环:

    1. while (SubLeft->_left)
    2. {
    3. Parent = SubLeft;
    4. SubLeft = SubLeft->_left;
    5. }

    三,实现二叉搜索树的全部代码

    1. #include
    2. using namespace std;
    3. #include
    4. template<class K>
    5. struct BNode
    6. {
    7. BNode(const K& key)
    8. :_key(key)
    9. ,_left(nullptr)
    10. ,_right(nullptr)
    11. {}
    12. K _key;
    13. BNode* _left;
    14. BNode* _right;
    15. };
    16. template<class K>
    17. class BSTree
    18. {
    19. public:
    20. bool Insert( const K& key)
    21. {
    22. return _Insert(key);
    23. }
    24. void Inorder()
    25. {
    26. _Inorder(_root);
    27. cout << endl;
    28. }
    29. bool Find(const K& key)
    30. {
    31. return _Find(key);
    32. }
    33. bool Erase(const K& key)
    34. {
    35. return _Erase(key);
    36. }
    37. private:
    38. bool _Insert(const K& key)
    39. {
    40. if (_root == nullptr)
    41. {
    42. _root = new BNode(key);
    43. return true;
    44. }
    45. else
    46. {
    47. BNode* cur = _root;
    48. BNode* parent = nullptr;
    49. while (cur!=nullptr)
    50. {
    51. parent = cur;
    52. if (cur->_key < key)
    53. {
    54. cur = cur->_right;
    55. }
    56. else if (cur->_key > key)
    57. {
    58. cur = cur->_left;
    59. }
    60. else
    61. {
    62. return false;
    63. }
    64. }
    65. cur = new BNode(key);
    66. if (key > parent->_key)
    67. {
    68. parent->_right = cur;
    69. }
    70. else
    71. {
    72. parent->_left = cur;
    73. }
    74. return true;
    75. }
    76. }
    77. bool _Erase(const K& key)
    78. {
    79. assert(_root);
    80. BNode* cur = _root;
    81. BNode* parent = cur;
    82. while (cur)
    83. {
    84. if (key > cur->_key)
    85. {
    86. parent = cur;
    87. cur = cur->_right;
    88. }
    89. else if (key < cur->_key)
    90. {
    91. parent = cur;
    92. cur = cur->_left;
    93. }
    94. else
    95. {
    96. if (cur->_left == nullptr)
    97. {
    98. if (cur == _root)
    99. {
    100. _root = _root->_right;
    101. }
    102. else
    103. {
    104. if (cur->_key > parent->_key)
    105. {
    106. parent->_right = cur->_right;
    107. }
    108. else
    109. {
    110. parent->_left = cur->_right;
    111. }
    112. }
    113. return true;
    114. }
    115. else if (cur->_right == nullptr)
    116. {
    117. if (cur == _root)
    118. {
    119. _root = cur->_left;
    120. }
    121. else
    122. {
    123. if (cur->_key > parent->_key)
    124. {
    125. parent->_right = cur->_left;
    126. }
    127. else
    128. {
    129. parent->_left = cur->_left;
    130. }
    131. }
    132. return true;
    133. }
    134. else
    135. {//有两个孩子,替换法。(找右子树的最左节点)
    136. BNode* Parent = cur;
    137. BNode* SubLeft = cur->_right;
    138. while (SubLeft->_left)
    139. {
    140. Parent = SubLeft;
    141. SubLeft = SubLeft->_left;
    142. }
    143. swap(cur->_key, SubLeft->_key);
    144. if (SubLeft == Parent->_left)
    145. {
    146. Parent->_left = SubLeft->_left;
    147. }
    148. else
    149. {
    150. Parent->_right = SubLeft->_left;
    151. }
    152. return true;
    153. }
    154. }
    155. }
    156. return false;
    157. }
    158. bool _Find(const K& key)
    159. {
    160. BNode* cur = _root;
    161. while (cur)
    162. {
    163. if (key > cur->_key)
    164. {
    165. cur = cur->_right;
    166. }
    167. else if (key < cur->_key)
    168. {
    169. cur = cur->_left;
    170. }
    171. else
    172. {
    173. return true;
    174. }
    175. }
    176. return false;
    177. }
    178. void _Inorder(BNode* root)
    179. {
    180. if (root == nullptr)
    181. {
    182. return;
    183. }
    184. _Inorder(root->_left);
    185. cout << root->_key << " ";
    186. _Inorder(root->_right);
    187. }
    188. BNode*_root =nullptr ;
    189. };

    实际上还可以实现一个递归版本的二叉搜索树,有时间再写。

  • 相关阅读:
    Spring实例化源码解析之Bean的实例化(十二)
    LeetCode 第 311 场周赛
    超越OpenCV速度的MorphologyEx函数实现(特别是对于二值图,速度是CV的4倍左右)。
    【分隔结构】同从分离
    STL——vector与迭代器
    【服务器数据恢复】RAID5强制上线后又被格式化的数据恢复案例
    【ES6】-- 对象、数组、字符串常用API
    JMeter数据库性能测试指南:全面掌握基础操作
    [附源码]计算机毕业设计springboot农村人居环境治理监管系统
    基础知识汇总一
  • 原文地址:https://blog.csdn.net/qq_41934502/article/details/134027109