当前位置:   article > 正文

C++ 实现二叉搜索树_c++二叉检索树

c++二叉检索树

一.二叉搜索树的基本概念

    二叉搜索树,也叫二叉排序树,是一种具有特殊性质的二叉树。

  • 若左子树不空,则左子树上所有节点的值都小于根节点的值。

  • 若右子树不空,则右子树上所有节点的值都大于根节点的值。

  • 任意节点的子树也都是二叉搜索树。

如下面这颗二叉树:

 我们可以看出这个二叉树符合上面的规则。

并且二叉搜索树还有一个重要的特点:

二叉搜索树的中序遍历,如果排序的是数字的话,那么他的中序遍历结果一定是个有序的数组,上面这个树的中序遍历为[1 15 16 17 18 20 23 24 27 28 30].

 二.key模型和key-value模型

概念:

  key的模型是指每个节点只有一个键值,用于确定节点在树中的位置。节点的键值必须满足二叉搜索树的性质,即左子节点的键值小于父节点的键值,右子节点的键值大于父节点的键值。这种模型比较简单,但是不能存储额外的信息。
  key/value模型是指每个节点有一个键值和一个数据值,键值用于确定节点在树中的位置,数据值用于存储节点的附加信息。节点的键值仍然必须满足二叉搜索树的性质,但是数据值可以是任意类型或对象。这种模型比较灵活,可以实现一些高级功能,比如映射或字典。

  一般而言,我们在程序中使用的都是key-value模型的二叉搜索树,因为key模型的没什么实际作用。

   假设你想要存储图书的话,在key/value模型中就可以让图书姓名当作键值,数量当作数据值,以此来详细统计一下图书。

   但是我们先由易到难,先来讲解一下key模型的二叉搜索树。

三.key模型的二叉搜索树操作

  3.1 节点

  无论是哪种模型,我们都需要定义一个内部类来表示节点,根据树的基本含义我们可以得知,除了key模型所需要的一个元素之外,我们还需要左右两个指针来表示每个节点的左孩子右孩子。

  故得出:

  1. template<typename K>
  2. class BSTreeNode //二叉搜索树的节点类
  3. {
  4. public:
  5. BSTreeNode(const K& key = K())
  6. :_left(nullptr)
  7. ,_right(nullptr)
  8. ,_key(key)
  9. {}
  10. BSTreeNode<K>* _left;
  11. BSTreeNode<K>* _right;
  12. K _key;
  13. };

  3.2 二叉树的表示

 在整个二叉树的类中,我们只要保证有一个头节点就好了,因为他会指向自己的左右孩子。

故得出:

  1. template<class K>
  2. class BSTree
  3. {
  4. typedef BSTreeNode<K> Node; //typedef一下节省代码量
  5. private
  6. Node* _root = nullptr;
  7. };

3.3 二叉搜索树的插入

3.3.1 思想

  二叉搜索树的插入其实只需要注意一点,那就是,插入后,仍然保持二叉搜索树的性质

  那么我们就要与原有的树上的值进行比较,到了合适的位置后再进行插入。 

  比如说这张图:

如果我们要插入的值为 19 的话,需要进行如下几段比较

因此,平衡二叉树的插入实际上就是一个 比较大小 直到寻找到空位置 再进行插入的过程 。

但是,如果插入的元素在数中已经有了该怎么办呢?

平衡二叉树的key模型基本上是拒绝重复元素的插入的。(难办就别办了)

3.3.2 代码实现 

  1. bool Insert(const K& key)
  2. {
  3. //特殊情况,如果头节点为nullptr,直接插入
  4. if (_root == nullptr)
  5. {
  6. _root = new Node(key);
  7. return true;
  8. }
  9. //记录父节点,以方便链接
  10. Node* parent = nullptr;
  11. //真正的遍历比较节点
  12. Node* cur = _root;
  13. //进行比较
  14. while (cur)
  15. {
  16. if (cur->_key < key)
  17. {
  18. parent = cur;
  19. cur = cur->_right;
  20. }
  21. else if (cur->_key > key)
  22. {
  23. parent = cur;
  24. cur = cur->_left;
  25. }
  26. else
  27. {
  28. //待插入的数在树中已经存在,直接不办了
  29. return false;
  30. }
  31. }
  32. //进行链接
  33. cur = new Node(key);
  34. //再次进行比较 判断插入的节点是在左边还是右边
  35. if (parent->_key < key)
  36. {
  37. parent->_right = cur;
  38. }
  39. else
  40. {
  41. parent->_left = cur;
  42. }
  43. return true;
  44. }

3.4 二叉搜索树的查找

3.4.1 简介

  可以说,二叉搜索树在查找上面的进步是很大,他的平均查找速度为O(logN),最坏查找情况为O(N)。(N为树的高度)

  为什么呢,其实类似于二分查找,它每次在查找的时候都只会前往左右区间,也就是直接剔除一半的值,最后便得出平均查找速度。

  最坏查找是因为有特殊情况,(一般是有序)使二叉树退化成单子树,从而大幅降低了查找效率。

如:

 3.4.2 思想

 其实查找也是个比较大小的思想,通过一次一次的比较大小,最终得出结果。

如:

我们要查找  18 

 3.4.3 代码实现

  1. Node* Find(const K& key) //我们选择返回节点指针
  2. {
  3. //通过父子指针进行查找与大小比较
  4. Node* parent = nullptr;
  5. Node* cur = _root;
  6. //进行大小比较
  7. while (cur)
  8. {
  9. if (cur->_key < key)
  10. {
  11. parent = cur;
  12. cur = cur->_right;
  13. }
  14. else if (cur->_key > key)
  15. {
  16. parent = cur;
  17. cur = cur->_left;
  18. }
  19. else
  20. {
  21. //与插入不同,查找到返回节点
  22. return cur;
  23. }
  24. }
  25. //没有查找到 返回空
  26. return nullptr;
  27. }

3.5 二叉搜索树的删除

3.5.1 思想

二叉搜索树的删除和插入一样,都需要保持一个原则,插入后,仍然保持二叉搜索树的性质

 二叉搜索树的删除无非就四种情况:

1.删除没有孩子节点的父节点

2.删除没有左孩子,但有右孩子的父节点。

3.删除没有有孩子,但有左孩子的节父点。

4.删除有两个孩子的父节点

还是经典老图,稍微改一下,我们分别要删除16 15 28 24这四个节点,分别对应四种情况,我们来看一下。

 

 

 

其实第一种情况可以归类为2.3种情况 

 3.5.2 代码实现

  1. bool Erase(const K& key)
  2. {
  3. //父指针记录 cur指针查找
  4. Node* parent = nullptr;
  5. Node* cur = _root;
  6. //while循环遍历查找
  7. while (cur)
  8. {
  9. if (cur->_key < key)
  10. {
  11. parent = cur;
  12. cur = cur->_right;
  13. }
  14. else if (cur->_key > key)
  15. {
  16. parent = cur;
  17. cur = cur->_left;
  18. }
  19. else
  20. {
  21. //查找到
  22. //开始删除
  23. //一共有三种情况
  24. //待删除节点左指针为nullptr
  25. if (cur->_left == nullptr) //第一二种 情况合起来
  26. {
  27. //处理特殊情况 如果要删除的是头指针
  28. if (_root == cur)
  29. {
  30. _root = cur->_right;
  31. }
  32. else
  33. {
  34. //如果是节点左右都为nullptr 父节点的指针就指向nullptr
  35. if (parent->_left == cur)
  36. {
  37. parent->_left = cur->_right;
  38. }
  39. else
  40. {
  41. parent->_left = cur->_right;
  42. }
  43. }
  44. delete cur;
  45. cur = nullptr;
  46. }
  47. //待删除节点右指针为nullptr
  48. else if (cur->_right == nullptr)
  49. {
  50. //处理特殊情况 如果要删除的是头指针
  51. if (_root == cur)
  52. {
  53. _root = cur->_left;
  54. }
  55. else
  56. {
  57. if (parent->_right == cur)
  58. {
  59. parent->_right = cur->_left;
  60. }
  61. else
  62. {
  63. parent->_right = cur->_left;
  64. }
  65. }
  66. delete cur;
  67. cur = nullptr;
  68. }
  69. //待删除节点左右孩子都有
  70. else
  71. {
  72. //查找左子树的最大值 其实就是查找cur左子树的最右节点
  73. /*Node* maxParent = cur;
  74. Node* max = cur->_left;
  75. while (max->_right)
  76. {
  77. maxParent = max;
  78. max =max->_left;
  79. }*/
  80. Node* minParent = cur;
  81. Node* min = cur->_right;
  82. //查找右子树的最小值 其实就是查找cur右子树的最左节点
  83. while (min->_left)
  84. {
  85. minParent = min;
  86. min = min->_left;
  87. }
  88. //将找到的值和cur交换
  89. swap(cur->_key, min->_key);
  90. //如果查找到的最大值或者最小值 还有另外的节点 需要链接
  91. if (minParent->_left == min)
  92. minParent->_left = min->_right;
  93. else
  94. minParent->_right = min->_right;
  95. delete min;
  96. min = nullptr;
  97. }
  98. return true;
  99. }
  100. }
  101. //没有删除返回false
  102. return false;
  103. }

3.6 中序遍历

没啥可说的 普通二叉树的遍历罢了,除了中序遍历可能是一个有序数组外这一个性质外。

  1. void _InOrder(Node* root)
  2. {
  3. if (root == nullptr)
  4. {
  5. return;
  6. }
  7. _InOrder(root->_left);
  8. cout << root->_key << endl;
  9. _InOrder(root->_right);
  10. }

3.7 总代码

  1. template<typename K>
  2. class BSTreeNode //二叉搜索树的节点类
  3. {
  4. public:
  5. BSTreeNode(const K& key = K())
  6. :_left(nullptr)
  7. ,_right(nullptr)
  8. ,_key(key)
  9. {}
  10. BSTreeNode<K>* _left;
  11. BSTreeNode<K>* _right;
  12. K _key;
  13. };
  14. template<class K>
  15. class BSTree
  16. {
  17. typedef BSTreeNode<K> Node; //typedef一下节省代码量
  18. public:
  19. bool Insert(const K& key)
  20. {
  21. //特殊情况,如果头节点为nullptr,直接插入
  22. if (_root == nullptr)
  23. {
  24. _root = new Node(key);
  25. return true;
  26. }
  27. //记录父节点,以方便链接
  28. Node* parent = nullptr;
  29. //真正的遍历比较节点
  30. Node* cur = _root;
  31. //进行比较
  32. while (cur)
  33. {
  34. if (cur->_key < key)
  35. {
  36. parent = cur;
  37. cur = cur->_right;
  38. }
  39. else if (cur->_key > key)
  40. {
  41. parent = cur;
  42. cur = cur->_left;
  43. }
  44. else
  45. {
  46. //待插入的数在树中已经存在,直接不办了
  47. return false;
  48. }
  49. }
  50. //进行链接
  51. cur = new Node(key);
  52. //再次进行比较 判断插入的节点是在左边还是右边
  53. if (parent->_key < key)
  54. {
  55. parent->_right = cur;
  56. }
  57. else
  58. {
  59. parent->_left = cur;
  60. }
  61. return true;
  62. }
  63. Node* Find(const K& key) //我们选择返回节点指针
  64. {
  65. //通过父子指针进行查找与大小比较
  66. Node* parent = nullptr;
  67. Node* cur = _root;
  68. //进行大小比较
  69. while (cur)
  70. {
  71. if (cur->_key < key)
  72. {
  73. parent = cur;
  74. cur = cur->_right;
  75. }
  76. else if (cur->_key > key)
  77. {
  78. parent = cur;
  79. cur = cur->_left;
  80. }
  81. else
  82. {
  83. //与插入不同,查找到返回节点
  84. return cur;
  85. }
  86. }
  87. //没有查找到 返回空
  88. return nullptr;
  89. }
  90. bool Erase(const K& key)
  91. {
  92. //父指针记录 cur指针查找
  93. Node* parent = nullptr;
  94. Node* cur = _root;
  95. //while循环遍历查找
  96. while (cur)
  97. {
  98. if (cur->_key < key)
  99. {
  100. parent = cur;
  101. cur = cur->_right;
  102. }
  103. else if (cur->_key > key)
  104. {
  105. parent = cur;
  106. cur = cur->_left;
  107. }
  108. else
  109. {
  110. //查找到
  111. //开始删除
  112. //一共有三种情况
  113. //待删除节点左指针为nullptr
  114. if (cur->_left == nullptr) //第一二种 情况合起来
  115. {
  116. //处理特殊情况 如果要删除的是头指针
  117. if (_root == cur)
  118. {
  119. _root = cur->_right;
  120. }
  121. else
  122. {
  123. //如果是节点左右都为nullptr 父节点的指针就指向nullptr
  124. if (parent->_left == cur)
  125. {
  126. parent->_left = cur->_right;
  127. }
  128. else
  129. {
  130. parent->_left = cur->_right;
  131. }
  132. }
  133. delete cur;
  134. cur = nullptr;
  135. }
  136. //待删除节点右指针为nullptr
  137. else if (cur->_right == nullptr)
  138. {
  139. //处理特殊情况 如果要删除的是头指针
  140. if (_root == cur)
  141. {
  142. _root = cur->_left;
  143. }
  144. else
  145. {
  146. if (parent->_right == cur)
  147. {
  148. parent->_right = cur->_left;
  149. }
  150. else
  151. {
  152. parent->_right = cur->_left;
  153. }
  154. }
  155. delete cur;
  156. cur = nullptr;
  157. }
  158. //待删除节点左右孩子都有
  159. else
  160. {
  161. //查找左子树的最大值 其实就是查找cur左子树的最右节点
  162. /*Node* maxParent = cur;
  163. Node* max = cur->_left;
  164. while (max->_right)
  165. {
  166. maxParent = max;
  167. max =max->_left;
  168. }*/
  169. Node* minParent = cur;
  170. Node* min = cur->_right;
  171. //查找右子树的最小值 其实就是查找cur右子树的最左节点
  172. while (min->_left)
  173. {
  174. minParent = min;
  175. min = min->_left;
  176. }
  177. //将找到的值和cur交换
  178. swap(cur->_key, min->_key);
  179. //如果查找到的最大值或者最小值 还有另外的节点 需要链接
  180. if (minParent->_left == min)
  181. minParent->_left = min->_right;
  182. else
  183. minParent->_right = min->_right;
  184. delete min;
  185. min = nullptr;
  186. }
  187. return true;
  188. }
  189. }
  190. //没有删除返回false
  191. return false;
  192. }
  193. void InOrder()
  194. {
  195. _InOrder(_root);
  196. cout << endl;
  197. }//为了预防数据被改,我们改成privat
  198. private:
  199. void _InOrder(Node* root)
  200. {
  201. if (root == nullptr)
  202. {
  203. return;
  204. }
  205. _InOrder(root->_left);
  206. cout << root->_key << endl;
  207. _InOrder(root->_right);
  208. }
  209. Node* _root = nullptr;
  210. };

四 .Key-Value模型的搜索二叉树

  如果说key模型的值只能表示在不在的话,那么key-value模型就可以表示通过一个值查找另一个值。

  key value模型 与上述实现的二叉搜索树实现功能差不多,只是增加了一个模板参数value。

代码为:

  1. template<class K, class V>
  2. class BSTreeNode
  3. {
  4. public:
  5. BSTreeNode(const K& key=K(),const V& val=V())
  6. :_left(nullptr)
  7. ,_right(nullptr)
  8. ,_key(key)
  9. ,_val(val)
  10. {
  11. }
  12. BSTreeNode<K, V>* _left;
  13. BSTreeNode<K, V>* _right;
  14. K _key;
  15. V _val;
  16. };
  17. template<class K, class V>
  18. class BSTree
  19. {
  20. typedef BSTreeNode<K, V> Node; //这里我们重命名一下,节省代码
  21. public:
  22. bool Insert(const K& key, const V& value)
  23. {
  24. if (_root == nullptr)
  25. {
  26. _root = new Node(key, value);
  27. return true;
  28. }
  29. Node* parent = nullptr;
  30. Node* cur = _root;
  31. while (cur)
  32. {
  33. if (cur->_key < key)
  34. {
  35. parent = cur;
  36. cur = cur->_right;
  37. }
  38. else if (cur->_key > key)
  39. {
  40. parent = cur;
  41. cur = cur->_left;
  42. }
  43. else
  44. {
  45. return false;
  46. }
  47. }
  48. cur = new Node(key, value);
  49. if (parent->_key < key)
  50. {
  51. parent->_right = cur;
  52. }
  53. else
  54. {
  55. parent->_left = cur;
  56. }
  57. return true;
  58. }
  59. Node* Find(const K& key)
  60. {
  61. Node* parent = nullptr;
  62. Node* cur = _root;
  63. while (cur)
  64. {
  65. if (cur->_key < key)
  66. {
  67. parent = cur;
  68. cur = cur->_right;
  69. }
  70. else if (cur->_key > key)
  71. {
  72. parent = cur;
  73. cur = cur->_left;
  74. }
  75. else
  76. {
  77. return cur;
  78. }
  79. }
  80. return nullptr;
  81. }
  82. bool Erase(const K& key)
  83. {
  84. Node* parent = nullptr;
  85. Node* cur = _root;
  86. while (cur)
  87. {
  88. if (cur->_key < key)
  89. {
  90. parent = cur;
  91. cur = cur->_right;
  92. }
  93. else if (cur->_key > key)
  94. {
  95. parent = cur;
  96. cur = cur->_left;
  97. }
  98. else
  99. {
  100. //开始删除
  101. //一共有三种情况
  102. if (cur->_left == nullptr)
  103. {
  104. if (_root == cur)
  105. {
  106. _root = cur->_right;
  107. }
  108. else
  109. {
  110. if (parent->_left == cur)
  111. {
  112. parent->_left = cur->_right;
  113. }
  114. else
  115. {
  116. parent->_left = cur->_right;
  117. }
  118. }
  119. delete cur;
  120. cur = nullptr;
  121. }
  122. else if (cur->_right == nullptr)
  123. {
  124. if (_root == cur)
  125. {
  126. _root = cur->_left;
  127. }
  128. else
  129. {
  130. if (parent->_right == cur)
  131. {
  132. parent->_right = cur->_left;
  133. }
  134. else
  135. {
  136. parent->_right = cur->_left;
  137. }
  138. }
  139. delete cur;
  140. cur = nullptr;
  141. }
  142. else
  143. {
  144. Node* minParent = cur;
  145. Node* min = cur->_right;
  146. while (min->_left)
  147. {
  148. minParent = min;
  149. min = min->_left;
  150. }
  151. swap(cur->_key, min->_key);
  152. if (minParent->_left == min)
  153. minParent->_left = min->_right;
  154. else
  155. minParent->_right = min->_right;
  156. delete min;
  157. min = nullptr;
  158. }
  159. return true;
  160. }
  161. }
  162. return false;
  163. }
  164. void InOrder()
  165. {
  166. _InOrder(_root);
  167. cout << endl;
  168. }
  169. private:
  170. void _InOrder(Node* root)
  171. {
  172. if (root == nullptr)
  173. {
  174. return;
  175. }
  176. _InOrder(root->_left);
  177. cout << root->_key << ":" << root->_val<< endl;
  178. _InOrder(root->_right);
  179. }
  180. Node* _root = nullptr;
  181. };

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

闽ICP备14008679号