• 数据结构 - 二叉搜索树


    目录

    一、概念

    二、实现 

    非递归删除

    递归删除

    三、总结


    一、概念

    二叉搜索树(BST,Binary Search Tree)

    也称二叉排序树,二叉查找树

    二叉搜索树:一棵二叉树,可以为空,如果不为空,满足以下性质:

    1. 非空左子树的所有键值小于其根节点的键值

    2. 非空右子树的所有键值大于其根节点的键值

    3. 左右子树都为二叉搜索树

    二、实现 

    1. //
    2. // Created by yangzilong on 2022/10/30.
    3. //
    4. #ifndef STL_BINARYSEARCHTREE_H
    5. #define STL_BINARYSEARCHTREE_H
    6. #include
    7. template <typename K>
    8. struct BSTreeNode
    9. {
    10. BSTreeNode(const K& key)
    11. :_key(key), _left(nullptr), _right(nullptr)
    12. { }
    13. BSTreeNode* _left;
    14. BSTreeNode* _right;
    15. K _key;
    16. };
    17. template <typename K>
    18. class BinarySearchTree {
    19. typedef BSTreeNode Node;
    20. private:
    21. Node* _root = nullptr;
    22. public:
    23. ~BinarySearchTree()
    24. {
    25. _Destroy(_root);
    26. }
    27. BinarySearchTree() = default;
    28. BinarySearchTree(const BinarySearchTree& t)
    29. {
    30. _root = _Copy(t._root);
    31. }
    32. BinarySearchTree& operator=(BinarySearchTree t)
    33. {
    34. std::swap(_root, t._root);
    35. return *this;
    36. }
    37. private:
    38. Node* _Copy(Node* root)
    39. {
    40. if(root == nullptr)
    41. return nullptr;
    42. Node* newNode = new Node(root->_key);
    43. newNode->_left = _Copy(root->_left);
    44. newNode->_right = _Copy(root->_right);
    45. return newNode;
    46. }
    47. void _Destroy(Node* root)
    48. {
    49. if(root == nullptr)
    50. return;
    51. _Destroy(root->_left);
    52. _Destroy(root->_right);
    53. delete root;
    54. }
    55. public:
    56. // 非递归
    57. bool Insert(const K& key) {
    58. // 空树
    59. if (_root == nullptr) {
    60. _root = new Node(key);
    61. return true;
    62. }
    63. Node *cur = _root;
    64. Node *parent = nullptr;
    65. while (cur) {
    66. if (cur->_key < key) {
    67. parent = cur;
    68. cur = cur->_right;
    69. } else if (cur->_key > key) {
    70. parent = cur;
    71. cur = cur->_left;
    72. } else {
    73. return false; // 已经存在了
    74. }
    75. }
    76. if (key > parent->_key) {
    77. parent->_right = new Node(key);
    78. } else {
    79. parent->_left = new Node(key);
    80. }
    81. return true;
    82. }
    83. bool Find(const K& key)
    84. {
    85. Node* cur = _root;
    86. while(cur)
    87. {
    88. if(key > cur->_key)
    89. {
    90. cur = cur->_right;
    91. }
    92. else if(key < cur->_key)
    93. {
    94. cur = cur->_left;
    95. }
    96. else
    97. {
    98. return true;
    99. }
    100. }
    101. return false;
    102. }
    103. bool Erase(const K& key) {
    104. Node *cur = _root;
    105. Node *parent = _root;
    106. while (cur) {
    107. if (key > cur->_key) {
    108. parent = cur;
    109. cur = cur->_right;
    110. } else if (key < cur->_key) {
    111. parent = cur;
    112. cur = cur->_left;
    113. } else {
    114. // edition 2
    115. if(cur->_left == nullptr)
    116. {
    117. if(parent->_left == cur)
    118. {
    119. parent->_left = cur->_right;
    120. delete cur;
    121. }
    122. else
    123. {
    124. parent->_right = cur->_right;
    125. delete cur;
    126. }
    127. }
    128. else if(cur->_right == nullptr)
    129. {
    130. if(parent->_left == cur)
    131. {
    132. parent->_left = cur->_left;
    133. delete cur;
    134. }
    135. else
    136. {
    137. parent->_right = cur->_left;
    138. delete cur;
    139. }
    140. }
    141. else
    142. {
    143. // 要删除结点的左右均不为空
    144. // 去删除结点的右子树中找最小值,替换法删除
    145. Node* min = cur->_right;
    146. Node* minParent = cur;
    147. while(min->_left)
    148. {
    149. minParent = min;
    150. min = min->_left;
    151. }
    152. std::swap(cur->_key, min->_key);
    153. if(min == minParent->_right)
    154. minParent->_right = min->_right;
    155. else
    156. minParent->_left = min->_right;
    157. delete min;
    158. }
    159. return true;
    160. // old
    161. // if (cur->_left == nullptr && cur->_right == nullptr) {
    162. // // 叶子节点,直接删除
    163. // if (parent->_left->_key == key) {
    164. // delete parent->_left;
    165. // parent->_left = nullptr;
    166. // } else {
    167. // delete parent->_right;
    168. // parent->_right = nullptr;
    169. // }
    170. // } else if (cur->_left != nullptr && cur->_right != nullptr) {
    171. // // 找右子树的最小值,和cur交换值
    172. // Node *minParent = cur;
    173. // Node *min = cur->_right; // child一定不为nullptr
    174. // while (min->_left) {
    175. // minParent = min;
    176. // min = min->_left;
    177. // }
    178. // std::swap(cur->_key, min->_key);
    179. // if (minParent == cur) {
    180. // minParent->_right = min->_right; // 此时child->_left一定为nullptr
    181. // delete min;
    182. // } else {
    183. // // 此时child和parent_2的关系一定是左子树和父节点
    184. // minParent->_left = min->_right;
    185. // delete min;
    186. // }
    187. // } else {
    188. // Node *child = nullptr;
    189. // if (cur->_right != nullptr) {
    190. // child = cur->_right;
    191. // } else {
    192. // child = cur->_left;
    193. // }
    194. // if (parent->_left != nullptr && parent->_left->_key == key) {
    195. // delete parent->_left;
    196. // parent->_left = child;
    197. // } else {
    198. // delete parent->_right;
    199. // parent->_right = child;
    200. // }
    201. // }
    202. // return true;
    203. }
    204. }
    205. return false;
    206. }
    207. void InOrder()
    208. {
    209. _InOrder(_root);
    210. std::cout<
    211. }
    212. private:
    213. void _InOrder(Node* root)
    214. {
    215. if(root == nullptr)
    216. return;
    217. _InOrder(root->_left);
    218. std::cout<_key<<" ";
    219. _InOrder(root->_right);
    220. }
    221. // 递归
    222. public:
    223. bool Find_R(const K& key)
    224. {
    225. // 最多找h次,h为树的高度。
    226. return _Find_R(_root, key);
    227. }
    228. bool Insert_R(const K& key)
    229. {
    230. return _Insert_R(_root, key);
    231. }
    232. bool Erase_R(const K& key)
    233. {
    234. return _Erase_R(_root, key);
    235. }
    236. private:
    237. bool _Erase_R(Node*& root, const K& key)
    238. {
    239. if(root == nullptr)
    240. {
    241. // 不存在
    242. return false;
    243. }
    244. if(key < root->_key)
    245. {
    246. return _Erase_R(root->_left, key);
    247. }
    248. else if(key > root->_key)
    249. {
    250. return _Erase_R(root->_right, key);
    251. }
    252. else
    253. {
    254. // 要删除的就是这个root,这个root实际上是父节点结构体里的right or left指针的别名!!!!!
    255. if(root->_left == nullptr) {
    256. Node* del = root;
    257. root = root->_right;
    258. delete del;
    259. }
    260. else if(root->_right == nullptr) {
    261. Node *del = root;
    262. root = root->_left;
    263. delete del;
    264. }
    265. else
    266. {
    267. Node* minParent = root;
    268. Node* min = root->_right;
    269. while(min->_left)
    270. {
    271. minParent = min;
    272. min = min->_left;
    273. }
    274. std::swap(root->_key, min->_key);
    275. // 上方交换时,root的key一定比min的key小,因为min在root的右子树中。
    276. // 此时交换完,root的key在右子树中一定符合二叉搜索树。
    277. // 并且下方递归调用时,一定会走左为空的情况。
    278. return _Erase_R(root->_right, key);
    279. // if(minParent->_left == min)
    280. // {
    281. // minParent->_left = min->_right;
    282. // }
    283. // else
    284. // {
    285. // minParent->_right = min->_right;
    286. // }
    287. // delete min;
    288. }
    289. return true;
    290. }
    291. }
    292. bool _Insert_R(Node*& root, const K& key)
    293. {
    294. if(root == nullptr)
    295. {
    296. root = new Node(key);
    297. return true;
    298. }
    299. if(key < root->_key)
    300. {
    301. // 这里是把结构体里的指针成员传过去,参数用引用接收,改变参数就是改变这里结构体的指针成员。
    302. return _Insert_R(root->_left, key);
    303. }
    304. else if(key > root->_key)
    305. {
    306. // 这里是把结构体里的指针成员传过去,参数用引用接收,改变参数就是改变这里结构体的指针成员。
    307. return _Insert_R(root->_right, key);
    308. }
    309. else
    310. {
    311. return false;
    312. }
    313. }
    314. // bool _Insert_R(Node* root, const K& key)
    315. // {
    316. // if(root == nullptr)
    317. // {
    318. // _root = new Node(key);
    319. // return true;
    320. // }
    321. // if(root->_key < key && root->_right == nullptr)
    322. // {
    323. // root->_right = new Node(key);
    324. // return true;
    325. // }
    326. // else if(root->_key > key && root->_left == nullptr)
    327. // {
    328. // root->_left = new Node(key);
    329. // return true;
    330. // }
    331. // else if(root->_key > key)
    332. // {
    333. // return _Insert_R(root->_left, key);
    334. // }
    335. // else if(root->_key < key)
    336. // {
    337. // return _Insert_R(root->_right, key);
    338. // }
    339. // else
    340. // {
    341. // return false;
    342. // }
    343. // }
    344. bool _Find_R(Node* root, const K& key)
    345. {
    346. if(root == nullptr)
    347. return false;
    348. if(root->_key > key)
    349. {
    350. return _Find_R(root->_left, key);
    351. }
    352. else if(root->_key < key)
    353. {
    354. return _Find_R(root->_right, key);
    355. }
    356. else
    357. {
    358. return true;
    359. }
    360. }
    361. };

    以上包含二叉搜索树的递归版与非递归版的插入,删除,查找。以及拷贝构造和析构的实现。

    唯一值得注意的就是删除了

    非递归删除

    先找到该结点,同时注意要记录要删除结点的父节点。

    分情况:

    1. 要删除结点的左右为空,即叶子结点

    2. 要删除结点的左为空

    3. 要删除结点的右为空

    4. 要删除结点的左右都为空

    1可以和2或3其中之一合并。

    若要删除结点(cur)的左为空,则将cur的右赋值给parent的左或右(取决于cur是parent的左还是右)

    若要删除结点(cur)的右为空,则将cur的左赋值给parent的左或右(取决于cur是parent的左还是右)

    1. // 要删除结点的左右均不为空
    2. // 去删除结点的右子树中找最小值,替换法删除
    3. Node* min = cur->_right;
    4. Node* minParent = cur;
    5. while(min->_left)
    6. {
    7. minParent = min;
    8. min = min->_left;
    9. }
    10. std::swap(cur->_key, min->_key);
    11. if(min == minParent->_right)
    12. minParent->_right = min->_right;
    13. else
    14. minParent->_left = min->_right;
    15. delete min;

    若要删除结点(cur)的左右均不为空,则查找cur的右子树中的最小结点(min),即图中的while循环),采用交换法,将cur的key和min的key交换(此时min的值放在cur的位置是符合二叉搜索树的性质的),此时要删除的结点就转换为了min。

    注意,此时要判断,min是cur的右结点还是右节点的左子树的某个结点。也就是while循环有没有执行。

    转换为上图,也就是

    若删除3(cur),则4是min,min是minparent的左。

    若删除8(cur),则10是min,min是minparent的右。

    不管怎样,min的左一定为空,直接将min的右(空or非空)赋值给min的左或右即可(取决于min是minparent的左还是右)

    递归删除

    1. bool _Erase_R(Node*& root, const K& key)
    2. {
    3. if(root == nullptr)
    4. {
    5. // 不存在
    6. return false;
    7. }
    8. if(key < root->_key)
    9. {
    10. return _Erase_R(root->_left, key);
    11. }
    12. else if(key > root->_key)
    13. {
    14. return _Erase_R(root->_right, key);
    15. }
    16. else
    17. {
    18. // 要删除的就是这个root,这个root实际上是父节点结构体里的right or left指针的别名!!!!!
    19. if(root->_left == nullptr) {
    20. Node* del = root;
    21. root = root->_right;
    22. delete del;
    23. }
    24. else if(root->_right == nullptr) {
    25. Node *del = root;
    26. root = root->_left;
    27. delete del;
    28. }
    29. else
    30. {
    31. Node* minParent = root;
    32. Node* min = root->_right;
    33. while(min->_left)
    34. {
    35. minParent = min;
    36. min = min->_left;
    37. }
    38. std::swap(root->_key, min->_key);
    39. // 上方交换时,root的key一定比min的key小,因为min在root的右子树中。
    40. // 此时交换完,root的key在右子树中一定符合二叉搜索树。
    41. // 并且下方递归调用时,一定会走左为空的情况。
    42. return _Erase_R(root->_right, key);
    43. // if(minParent->_left == min)
    44. // {
    45. // minParent->_left = min->_right;
    46. // }
    47. // else
    48. // {
    49. // minParent->_right = min->_right;
    50. // }
    51. // delete min;
    52. }
    53. return true;
    54. }
    55. }

    除了是基于递归实现的,这里的基本原理和非递归一样,只是在交换完cur和min的key之后,直接在cur的右子树中删除key即可(此时key在min结点中)。最终会递归到左子树为空的情况,因为min->left == nullptr

    三、总结

    若二叉搜索树接近完全二叉树,也就是高度接近log(N),则二叉搜索树的效率会很高。

    若二叉搜索树的构建过程中,元素有序或者接近有序,则BST的查找,删除,插入的效率都会很低,接近O(N),故引出AVL树,红黑树来控制搜索树的高度。

  • 相关阅读:
    740.删除并获得点数
    接口(接口相关含义,区别抽象类,接口回调)
    【数据结构】红黑树
    CRM自动化意味着什么?企业如何从中受益?
    目标检测的yolov3、4、5、6总结
    uoj#750-[UNR #6]小火车【二分,折半,鸽笼原理】
    企业网络“卫生”实用指南(上)
    深度学习篇之tensorflow(3) ---架构介绍篇二
    File类和IO流的相关面试(二)
    《Mycat分布式数据库架构》之Mycat管理
  • 原文地址:https://blog.csdn.net/i777777777777777/article/details/127785885