• C++搜索二叉树


    一、搜索二叉树概念

    搜索二叉树是一种树形结构,常用于map当中。搜索二叉树严格遵守左小右大的规则

    C语言中实现搜索二叉树有一些困难,并且在面对一些特定题目实现较困难。因此采用C++的方式再次实现搜索二叉树

    二、搜索二叉树的实现

    插入

     搜索二叉树在插入之前必须要先遍历查找到合适的位置,再找到合适的位置后将结点插入。这里需要考虑如何将插入节点与原二叉树连接,因此需要同时记录父节点以方便连接

    1. bool Insert(const K& key)
    2. {
    3. if (_root == nullptr)
    4. {
    5. _root = new Node(key);
    6. return true;
    7. }
    8. Node* parent = nullptr;
    9. Node* cur = _root;
    10. while (cur)
    11. {
    12. if (cur->_key < key)
    13. {
    14. parent = cur;
    15. cur = cur->_right;
    16. }
    17. else if (cur->_key > key)
    18. {
    19. parent = cur;
    20. cur = cur->_left;
    21. }
    22. else
    23. {
    24. return false;
    25. }
    26. }
    27. //将插入节点与树连接
    28. cur = new Node(key);
    29. if (parent->_key < key)
    30. {
    31. parent->_right = cur;
    32. }
    33. else
    34. {
    35. parent->_left = cur;
    36. }
    37. return true;
    38. }

    删除

    删除需要考虑的情况比插入更复杂:

    ①在叶子节点删除

    下图中4、6、10都是叶子节点,删除叶子节点的处理比较简单,直接删除即可

    ②删除有左孩子或者有右孩子的节点

    寻找删除节点的父节点,继承删除节点的子节点

    这里需要考虑删除根节点的问题,如果根节点只有单一子树,由于根节点并没有父节点,会出现空指针问题

    因此如果删除节点为根节点,并且根节点只有单一子树,则更新根节点(采用替代法也可以,但是更新根节点更简单)

    ③删除同时有左孩子和右孩子的节点

    只能间接删除,同时有两个孩子,就只能采用替代法。选择删除节点左子树上的最大节点或者右子树上的最小节点替代该节点,因为需要保持左小右大的规则

    1. bool Erase(const K& key)
    2. {
    3. Node* parent = nullptr;
    4. Node* cur = _root;
    5. while (cur)
    6. {
    7. if (cur->_key < key)
    8. {
    9. parent = cur;
    10. cur = cur->_right;
    11. }
    12. else if (cur->_key > key)
    13. {
    14. parent = cur;
    15. cur = cur->_left;
    16. }
    17. else
    18. {
    19. if(cur->_left==nullptr)//单个孩子左为空
    20. {
    21. if (cur == _root)
    22. {
    23. _root = cur->_right;
    24. }
    25. else
    26. {
    27. if (parent->_left == cur)
    28. {
    29. parent->_left = cur->_right;
    30. }
    31. else
    32. {
    33. parent->_right = cur->_right;
    34. }
    35. }
    36. delete cur;
    37. }
    38. else if(cur->_right==nullptr)//单个孩子右为空
    39. {
    40. if (cur == _root)
    41. {
    42. _root = cur->_left;
    43. //如果删除根节点,由于根节点没有父节点,因此直接更新根节点
    44. }
    45. else
    46. {
    47. if (parent->_left == cur)
    48. {
    49. parent->_left = cur->_left;
    50. }
    51. else
    52. {
    53. parent->_right = cur->_left;
    54. }
    55. }
    56. delete cur;
    57. }
    58. else//同时有两个孩子
    59. {
    60. //找要删除节点的右树最小节点或左树最大节点替代
    61. Node* minRight = cur->_right;//右树最小
    62. Node* pminRight = cur;//minRight的父节点
    63. //这里不赋值为nullptr也是考虑到如果删除根节点的空指针问题
    64. while (minRight->_left)//最小需要往左找
    65. {
    66. pminRight = minRight;
    67. minRight = minRight->_left;
    68. }
    69. cur->_key = minRight->_key;//替代
    70. //pminRight->_left = minRight->_right;对于根节点会有空指针问题
    71. if (pminRight->_left == minRight)//考虑根节点删除问题
    72. {
    73. pminRight->_left = minRight->_right;
    74. }
    75. else
    76. {
    77. pminRight->_right = minRight->_right;
    78. }
    79. delete minRight;
    80. }
    81. return true;
    82. }
    83. }
    84. return false;
    85. }

    整体实现

    1. #pragma once
    2. #include
    3. using namespace std;
    4. template<class K>
    5. struct BSTreeNode
    6. {
    7. BSTreeNode* _right;
    8. BSTreeNode* _left;
    9. K _key;
    10. BSTreeNode(const K& key)
    11. :_left(nullptr),_right(nullptr),_key(key)
    12. {}
    13. };
    14. template<class K>
    15. class BSTree
    16. {
    17. typedef BSTreeNode Node;
    18. public:
    19. BSTree() = default;//强制生成默认构造
    20. BSTree(const BSTree& t)//拷贝构造
    21. {
    22. _root = Copy(t._root);
    23. }
    24. BSTree& operator=(BSTree t)//赋值构造
    25. {
    26. swap(_root, t._root);
    27. return *this;
    28. }
    29. ~BSTree()
    30. {
    31. Destory(_root);
    32. }
    33. bool Insert(const K& key)
    34. {
    35. if (_root == nullptr)
    36. {
    37. _root = new Node(key);
    38. return true;
    39. }
    40. Node* parent = nullptr;
    41. Node* cur = _root;
    42. while (cur)
    43. {
    44. if (cur->_key < key)
    45. {
    46. parent = cur;
    47. cur = cur->_right;
    48. }
    49. else if (cur->_key > key)
    50. {
    51. parent = cur;
    52. cur = cur->_left;
    53. }
    54. else
    55. {
    56. return false;
    57. }
    58. }
    59. //将插入节点与树连接
    60. cur = new Node(key);
    61. if (parent->_key < key)
    62. {
    63. parent->_right = cur;
    64. }
    65. else
    66. {
    67. parent->_left = cur;
    68. }
    69. return true;
    70. }
    71. bool Find(const K& key)
    72. {
    73. Node* cur = _root;
    74. while (cur)
    75. {
    76. if (cur->_key > key)
    77. {
    78. cur = cur->_left;
    79. }
    80. else if (cur->_key < key)
    81. {
    82. cur = cur->_right;
    83. }
    84. else
    85. {
    86. return true;
    87. }
    88. }
    89. return false;
    90. }
    91. bool Erase(const K& key)
    92. {
    93. Node* parent = nullptr;
    94. Node* cur = _root;
    95. while (cur)
    96. {
    97. if (cur->_key < key)
    98. {
    99. parent = cur;
    100. cur = cur->_right;
    101. }
    102. else if (cur->_key > key)
    103. {
    104. parent = cur;
    105. cur = cur->_left;
    106. }
    107. else
    108. {
    109. if(cur->_left==nullptr)//单个孩子左为空
    110. {
    111. if (cur == _root)
    112. {
    113. _root = cur->_right;
    114. }
    115. else
    116. {
    117. if (parent->_left == cur)
    118. {
    119. parent->_left = cur->_right;
    120. }
    121. else
    122. {
    123. parent->_right = cur->_right;
    124. }
    125. }
    126. delete cur;
    127. }
    128. else if(cur->_right==nullptr)//单个孩子右为空
    129. {
    130. if (cur == _root)
    131. {
    132. _root = cur->_left;
    133. //如果删除根节点,由于根节点没有父节点,因此直接更新根节点
    134. }
    135. else
    136. {
    137. if (parent->_left == cur)
    138. {
    139. parent->_left = cur->_left;
    140. }
    141. else
    142. {
    143. parent->_right = cur->_left;
    144. }
    145. }
    146. delete cur;
    147. }
    148. else//同时有两个孩子
    149. {
    150. //找要删除节点的右树最小节点或左树最大节点替代
    151. Node* minRight = cur->_right;//右树最小
    152. Node* pminRight = cur;//minRight的父节点
    153. //这里不赋值为nullptr也是考虑到如果删除根节点的空指针问题
    154. while (minRight->_left)//最小需要往左找
    155. {
    156. pminRight = minRight;
    157. minRight = minRight->_left;
    158. }
    159. cur->_key = minRight->_key;//替代
    160. //pminRight->_left = minRight->_right;对于根节点会有空指针问题
    161. if (pminRight->_left == minRight)//考虑根节点删除问题
    162. {
    163. pminRight->_left = minRight->_right;
    164. }
    165. else
    166. {
    167. pminRight->_right = minRight->_right;
    168. }
    169. delete minRight;
    170. }
    171. return true;
    172. }
    173. }
    174. return false;
    175. }
    176. //递归实现封装接口
    177. bool FindR(const K& key)
    178. {
    179. return _FindR(_root, key);
    180. }
    181. bool InsertR(const K& key)
    182. {
    183. return _InsertR(_root, key);
    184. }
    185. bool EraseR(const K& key)
    186. {
    187. return _EraseR(_root, key);
    188. }
    189. void InOrder()//二次封装,方便调用
    190. {
    191. _InOrder(_root);
    192. cout << endl;
    193. }
    194. protected://递归实现
    195. void Destory(Node*& root)
    196. {
    197. if (root == nullptr)
    198. {
    199. return;
    200. }
    201. Destory(root->_left);
    202. Destory(root->_right);
    203. delete root;
    204. root = nullptr;
    205. }
    206. Node* Copy(Node*& root)
    207. {
    208. if (root == nullptr)
    209. {
    210. return nullptr;
    211. }
    212. Node* newroot = new Node(root->_key);
    213. newroot->_left = Copy(root->_left);
    214. newroot->_right = Copy(root->_right);
    215. return newroot;
    216. }
    217. void _InOrder(Node* root)
    218. {
    219. if (root == nullptr)
    220. {
    221. return;
    222. }
    223. _InOrder(root->_left);
    224. cout << root->_key << " ";
    225. _InOrder(root->_right);
    226. }
    227. bool _FindR(Node* root, const K& key)
    228. {
    229. if (root == nullptr)
    230. {
    231. return false;
    232. }
    233. if (root->_key = key)
    234. {
    235. return true;
    236. }
    237. if (root->_key < key)
    238. {
    239. return _FindR(root->_right, key);
    240. }
    241. else
    242. return _FindR(root->_left, key);
    243. }
    244. bool _InsertR(Node*& root, const K& key)
    245. //当走到空节点时,root是上一次调用的引用(root->_right或root->_left的引用)
    246. {
    247. if (root == nullptr)
    248. {
    249. root = new Node(key);
    250. //此时的root节点本就在树中,只不过为空。因此可以直接创建新节点而无需连接
    251. return true;
    252. }
    253. if (root->_key < key)
    254. {
    255. return _InsertR(root->_right, key);
    256. }
    257. else if (root->_key > key)
    258. {
    259. return _InsertR(root->_left, key);
    260. }
    261. else
    262. {
    263. return false;
    264. }
    265. }
    266. bool _EraseR(Node*& root, const K& key)
    267. {
    268. if (root == nullptr)
    269. {
    270. return false;
    271. }
    272. if (root->_key < key)
    273. {
    274. return _EraseR(root->_right,key);
    275. }
    276. else if (root->_key > key)
    277. {
    278. return _EraseR(root->_left, key);
    279. }
    280. else
    281. {
    282. Node* del = root;
    283. if (root->_left == nullptr)
    284. {
    285. root = root->_right;
    286. }
    287. else if (root->_right == nullptr)
    288. {
    289. root = root->_left;
    290. }
    291. else
    292. {
    293. Node* maxleft = root->_left;
    294. while (maxleft->_right)
    295. {
    296. maxleft = maxleft->_right;
    297. }
    298. swap(root->_key , maxleft->_key);
    299. return _EraseR(_root->_left, key);
    300. //转换为子树删除,因为一定是右为空
    301. //这里不能是maxleft因为是引用传参
    302. }
    303. delete del;
    304. return true;
    305. }
    306. }
    307. private:
    308. Node* _root = nullptr;
    309. };

    搜索二叉树的应用 

    1.key模型

    K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值

    实际应用:车库比对车牌号,人脸识别

    2.key value模型 

    每一个关键码key,都有与之对应的值Value,即的键值对  

    实际应用:英汉词典中英相互对应、学生姓名与学号相互对应 

    1. #pragma once
    2. //搜索二叉树的key,value模型
    3. #include
    4. using namespace std;
    5. template<class K, class V>
    6. struct BSTreeNode
    7. {
    8. BSTreeNode* _left;
    9. BSTreeNode* _right;
    10. K _key;
    11. V _value;
    12. BSTreeNode(const K& key, const V& value)
    13. :_left(nullptr)
    14. , _right(nullptr)
    15. , _key(key)
    16. , _value(value)
    17. {}
    18. };
    19. template<class K, class V>
    20. class BSTree
    21. {
    22. typedef BSTreeNode Node;
    23. public:
    24. bool Insert(const K& key, const V& value)
    25. {
    26. if (_root == nullptr)
    27. {
    28. _root = new Node(key, value);
    29. return true;
    30. }
    31. Node* parent = nullptr;
    32. Node* cur = _root;
    33. while (cur)
    34. {
    35. if (cur->_key < key)
    36. {
    37. parent = cur;
    38. cur = cur->_right;
    39. }
    40. else if (cur->_key > key)
    41. {
    42. parent = cur;
    43. cur = cur->_left;
    44. }
    45. else
    46. {
    47. return false;
    48. }
    49. }
    50. cur = new Node(key, value);
    51. // 链接
    52. if (parent->_key < key)
    53. {
    54. parent->_right = cur;
    55. }
    56. else
    57. {
    58. parent->_left = cur;
    59. }
    60. return true;
    61. }
    62. Node* Find(const K& key)
    63. {
    64. Node* cur = _root;
    65. while (cur)
    66. {
    67. if (cur->_key < key)
    68. {
    69. cur = cur->_right;
    70. }
    71. else if (cur->_key > key)
    72. {
    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. // 1、左为空
    102. if (cur->_left == nullptr)
    103. {
    104. if (cur == _root)
    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->_right = cur->_right;
    117. }
    118. }
    119. delete cur;
    120. } // 2、右为空
    121. else if (cur->_right == nullptr)
    122. {
    123. if (cur == _root)
    124. {
    125. _root = cur->_left;
    126. }
    127. else
    128. {
    129. if (parent->_left == cur)
    130. {
    131. parent->_left = cur->_left;
    132. }
    133. else
    134. {
    135. parent->_right = cur->_left;
    136. }
    137. }
    138. delete cur;
    139. }
    140. else
    141. {
    142. // 找右树最小节点替代,也可以是左树最大节点替代
    143. Node* pminRight = cur;
    144. Node* minRight = cur->_right;
    145. while (minRight->_left)
    146. {
    147. pminRight = minRight;
    148. minRight = minRight->_left;
    149. }
    150. cur->_key = minRight->_key;
    151. if (pminRight->_left == minRight)
    152. {
    153. pminRight->_left = minRight->_right;
    154. }
    155. else
    156. {
    157. pminRight->_right = minRight->_right;
    158. }
    159. delete minRight;
    160. }
    161. return true;
    162. }
    163. }
    164. return false;
    165. }
    166. void InOrder()
    167. {
    168. _InOrder(_root);
    169. }
    170. protected:
    171. void _InOrder(Node* root)
    172. {
    173. if (root == nullptr)
    174. return;
    175. _InOrder(root->_left);
    176. cout << root->_key << ":" << root->_value<<" ";
    177. _InOrder(root->_right);
    178. }
    179. private:
    180. Node* _root = nullptr;
    181. };

    搜索二叉树性能分析

    插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能

    对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二 叉搜索树的深度的函数,即结点越深,则比较次数越多

    最优情况最坏情况
    树的形态搜索二叉树接近完全二叉树搜索二叉树为单边树
    时间复杂度

    由于搜索二叉树的形态不确定,导致其时间复杂度也取决于二叉树的形态(比如单边树)因此出现了平衡搜索二叉树,与搜索二叉树的不同就在于平衡。始终将二叉树调整到接近完全二叉树,使得时间复杂度为最优解

  • 相关阅读:
    黑客入侵机构,导致2万条信息被卖
    Jupyter Notebook 入门教程
    泛型的介绍和使用方法
    Kotlin--Sealed Class Sealed Interface
    【C++】set和map的底层结构(AVL树&红黑树)
    解决mysql Packet for query is too large
    基于php旅游网站管理系统获取(php毕业设计)
    TS内容学习总结
    Mac SpringBoot项目 Gradle 7.3 转 Maven 手把手教学,包学会~
    Python实现九九乘法表的几种方式,入门必备案例~超级简单~
  • 原文地址:https://blog.csdn.net/RXY24601/article/details/130900444