• C++——二叉搜索树


    目录

    二叉搜索树

    二叉搜索树实现 

    非递归插入|非递归查找

    删除 

    推导阶段 

    非递归删除代码 

    递归查找 

     递归插入

    递归删除 

    析构函数 

    拷贝构造 

    赋值重载

    完整代码 

     二叉搜索树的应用

    Key/Value模型 


     

    二叉搜索树

     二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
        若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
        若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
        它的左右子树也分别为二叉搜索树

    二叉搜索树实现 

    非递归插入|非递归查找

    1. #include
    2. using namespace std;
    3. template<class K>
    4. class BStreeNode
    5. {
    6. public:
    7. BStreeNode(const K& key)
    8. :_left(nullptr),
    9. _right(nullptr),
    10. _key(key)
    11. {}
    12. BStreeNode* _left;
    13. BStreeNode* _right;
    14. K _key;
    15. };
    16. template<class K>
    17. class BStree
    18. {
    19. typedef BStreeNode Node;
    20. public:
    21. bool Insert(const K& key)
    22. {
    23. if (_root == nullptr)
    24. {
    25. _root = new Node(key);
    26. return true;
    27. }
    28. Node* parent = nullptr;
    29. Node* cur = _root;
    30. while (cur)
    31. {
    32. if (cur->_key < key)
    33. {
    34. parent = cur;
    35. cur = cur->_right;
    36. }
    37. else if (cur->_key > key)
    38. {
    39. parent = cur;
    40. cur = cur->_left;
    41. }
    42. else
    43. {
    44. return false;
    45. }
    46. }
    47. cur = new Node(key);
    48. if (parent->_key < key)
    49. {
    50. parent->_right = cur;
    51. }
    52. else
    53. {
    54. parent->_left = cur;
    55. }
    56. return true;
    57. }
    58. bool Find(const K& key)//查找
    59. {
    60. Node* cur = _root;
    61. while (cur)
    62. {
    63. if (cur->_key < key)
    64. {
    65. cur = cur->_right;
    66. }
    67. else if (cur->_key > key)
    68. {
    69. cur = cur->_left;
    70. }
    71. else
    72. {
    73. return true;
    74. }
    75. }
    76. return true;
    77. }
    78. void InOrder()
    79. {
    80. _InOrder(_root);
    81. }
    82. private:
    83. void _InOrder(Node *root)
    84. {
    85. if (root == nullptr)
    86. return;
    87. _InOrder(root->_left);
    88. cout << root->_key << " ";
    89. _InOrder(root->_right);
    90. }
    91. private:
    92. Node* _root = nullptr;
    93. };
    94. int main()
    95. {
    96. BStree<int> t;
    97. int a[] = { 1,1,2,2,3,6,165,132,4185,123 };
    98. for (auto e : a)
    99. {
    100. t.Insert(e);
    101. }
    102. t.InOrder();
    103. return 0;
    104. }

     

    可去重 

    删除 

    推导阶段 

     1.若要删除的节点是叶子节点,直接删除即可

    2.删除节点只有一个孩子

    若左为空,则让父亲节点指向该节点的右子树以删除3为例

    若果要删除跟节点,而且左为空,若要删除8,我们更新根节点即可,让根节点指向10

    若右为空,则让父亲指向左子树,以删除14为例

      若果要删除跟节点,而且右为空,若要删除8,让根节点指向3即可

     3.要删除的节点其左右节点都不为空

    我们可以采用替换法删除节点

    用左子树的最大节1点或右子树的最小节点4,若采用右树的最小节点,交换3和4删除4之后,删除3,但还有一个子节点5,我们让5成为6的左节点

    若要删除8,这里采用右树最左节点的替换法,右树的最左节点就是10自己,如果这样写会有错误,while循环都不会进去,minparent就是空,而后面minParent->_left = min->_right;这个语句会出错,修正方法,让minparent一开始就是cur,并且加个判断。

    这样写即可

    非递归删除代码 

    1. bool Erase(const K& key)//删除
    2. {
    3. //若有一个子节点,删除父节点后,让子节点填充
    4. //若有俩个子节点,父节点删除后
    5. //1.用左子树的最大节点替换父节点
    6. //2.或右子树的最小节点替换父节点
    7. Node* parent = nullptr;
    8. Node* cur = _root;
    9. while (cur)
    10. {
    11. if (cur->_key > key)
    12. {
    13. parent = cur;
    14. cur = cur->_left;
    15. }
    16. else if (cur->_key < key)
    17. {
    18. parent = cur;
    19. cur = cur->_right;
    20. }
    21. else//找到了
    22. {
    23. if (cur->_left == nullptr)//如果要删除的节点左为空
    24. {
    25. if (cur == _root)//如果要删除的是根节点(这种情况根节点只有右子树,因为左为空)
    26. {
    27. _root = cur->_right;
    28. }
    29. else
    30. {
    31. if (cur == parent->_left)//判断要删除的节点是父亲的左节点还是右节点
    32. {
    33. parent->_left = cur->_right;
    34. }
    35. else
    36. {
    37. parent->_right = cur->_right;
    38. }
    39. }
    40. delete cur;
    41. cur = nullptr;
    42. }
    43. else if (cur->_right == nullptr)//如果要删除的节点右为空
    44. {
    45. if (cur == _root)
    46. {
    47. _root = cur->_left;
    48. }
    49. else
    50. {
    51. if (cur == parent->_left)//判断要删除的节点是父亲的左节点还是右节点
    52. {
    53. parent->_left = cur->_left;
    54. }
    55. else
    56. {
    57. parent->_right = cur->_left;
    58. }
    59. }
    60. delete cur;
    61. cur = nullptr;
    62. }
    63. else//左右都为空,叶子节点,这里采用用右树的最小节点进行删除
    64. {
    65. Node* minParent = cur;
    66. Node*min = cur->_right;//cur是要删除的节点
    67. while (min->_left)//寻找最小节点
    68. {
    69. minParent = min;
    70. min = min->_left;
    71. }
    72. swap(cur->_key, min->_key);
    73. if (minParent->_left == min)
    74. {
    75. minParent->_left = min->_right;
    76. }
    77. else
    78. minParent->_right = min->_right;
    79. delete min;
    80. }
    81. return true;
    82. }
    83. }
    84. return false;
    85. }

    递归查找 

    1. bool _FindR(Node *root,const K& key)
    2. {
    3. if (root == nullptr)
    4. return false;
    5. if (root->_key < key)
    6. {
    7. return _FindR(root->right, key);
    8. }
    9. else if (root->_key > key)
    10. {
    11. return _FindR(root->left, key);
    12. }
    13. else
    14. {
    15. return true;
    16. }
    17. }

     递归插入

    这种插入写法会导致二叉树断开

     这里的Node没有跟父节点连接上,而是创建了一个空间单独在那里

     加上引用即可

    1. bool _InsertR(Node*& root, const K& key)
    2. {
    3. if (root == nullptr)//根为空,直接插入
    4. {
    5. root = new Node(key);
    6. return true;
    7. }
    8. if (root->_key < key)
    9. {
    10. return _InsertR(root->_right, key);
    11. }
    12. else if (root->_key > key)
    13. {
    14. return _InsertR(root->_left, key);
    15. }
    16. else
    17. {
    18. return false;
    19. }
    20. }

    递归删除 

    1. bool _Eraser(Node*& root, const K& key)
    2. {
    3. if (root == nullptr)
    4. return false;
    5. if (root->_key < key)
    6. {
    7. return _Eraser(root->_right, key);
    8. }
    9. else if (root->_key > key)
    10. {
    11. return _Eraser(root->_left, key);
    12. }
    13. else
    14. {
    15. Node* del = root;
    16. if (root->_left == nullptr)
    17. {
    18. root = root->_right;
    19. }
    20. else if (root->_right == nullptr)
    21. {
    22. root = root->_left;//由于是引用,可直接这样将二叉树连接起来
    23. }
    24. else
    25. {
    26. //找右树的最左节点
    27. Node* min = root->_right;
    28. while (min->_left)
    29. {
    30. min = min->_left;
    31. }
    32. swap(root->_key, min->_key);
    33. return _Eraser(root->_right, key);
    34. }
    35. delete del;
    36. return true;
    37. }
    38. }

    析构函数 

    1. ~BStree()
    2. {
    3. Destory(_root);
    4. }
    5. private:
    6. void Destory(Node*& root)//采用引用可让root置空起作用
    7. {
    8. if (root ==nullptr)
    9. return;
    10. Destory(root->_left);
    11. Destory(root->right);
    12. delete root;
    13. root=nullptr
    14. }

    拷贝构造 

     注:拷贝构造也是构造,如果写了构造,编译器不会生成默认构造,会报错没有合适的默认构造

    1. BStree(const BStree & t)
    2. {
    3. _root = _Copy(t._root);
    4. }
    5. Node* _Copy(Node* root)
    6. {
    7. if (root == nullptr)
    8. {
    9. return nullptr;
    10. }
    11. Node* copyRoot = new Node(root->_key);
    12. copyRoot->_left = _Copy(root->_left);
    13. copyRoot->_right = _Copy(root->_right);
    14. return copyRoot;
    15. }

    我们需加默认构造 

    默认构造也可写为BSTree()=default;

    这是强制编译器生成默认构造,是C++11的用法

    赋值重载

    1. BStree& operator=(BStree t)
    2. {
    3. swap(_root, t._root);
    4. return *this;
    5. }

    搜索二叉树增删查的时间复杂度是:O(h),h代表高度 

    完整代码 

    1. #include
    2. using namespace std;
    3. template<class K>
    4. class BStreeNode
    5. {
    6. public:
    7. BStreeNode(const K& key)
    8. :_left(nullptr),
    9. _right(nullptr),
    10. _key(key)
    11. {}
    12. BStreeNode* _left;
    13. BStreeNode* _right;
    14. K _key;
    15. };
    16. template<class K>
    17. class BStree
    18. {
    19. typedef BStreeNode Node;
    20. public:
    21. bool Insert(const K& key)
    22. {
    23. if (_root == nullptr)
    24. {
    25. _root = new Node(key);
    26. return true;
    27. }
    28. Node* parent = nullptr;
    29. Node* cur = _root;
    30. while (cur)
    31. {
    32. if (cur->_key < key)
    33. {
    34. parent = cur;
    35. cur = cur->_right;
    36. }
    37. else if (cur->_key > key)
    38. {
    39. parent = cur;
    40. cur = cur->_left;
    41. }
    42. else
    43. {
    44. return false;
    45. }
    46. }
    47. cur = new Node(key);
    48. if (parent->_key < key)
    49. {
    50. parent->_right = cur;
    51. }
    52. else
    53. {
    54. parent->_left = cur;
    55. }
    56. return true;
    57. }
    58. void InOrder()//排序
    59. {
    60. _InOrder(_root);
    61. }
    62. bool 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 true;
    78. }
    79. }
    80. return true;
    81. }
    82. bool Erase(const K& key)//删除
    83. {
    84. //若有一个子节点,删除父节点后,让子节点填充
    85. //若有俩个子节点,父节点删除后
    86. //1.用左子树的最大节点替换父节点
    87. //2.或右子树的最小节点替换父节点
    88. Node* parent = nullptr;
    89. Node* cur = _root;
    90. while (cur)
    91. {
    92. if (cur->_key > key)
    93. {
    94. parent = cur;
    95. cur = cur->_left;
    96. }
    97. else if (cur->_key < key)
    98. {
    99. parent = cur;
    100. cur = cur->_right;
    101. }
    102. else//找到了
    103. {
    104. if (cur->_left == nullptr)//如果要删除的节点左为空
    105. {
    106. if (cur == _root)//如果要删除的是根节点(这种情况根节点只有右子树,因为左为空)
    107. {
    108. _root = cur->_right;
    109. }
    110. else
    111. {
    112. if (cur == parent->_left)//判断要删除的节点是父亲的左节点还是右节点
    113. {
    114. parent->_left = cur->_right;
    115. }
    116. else
    117. {
    118. parent->_right = cur->_right;
    119. }
    120. }
    121. delete cur;
    122. cur = nullptr;
    123. }
    124. else if (cur->_right == nullptr)//如果要删除的节点右为空
    125. {
    126. if (cur == _root)
    127. {
    128. _root = cur->_left;
    129. }
    130. else
    131. {
    132. if (cur == parent->_left)//判断要删除的节点是父亲的左节点还是右节点
    133. {
    134. parent->_left = cur->_left;
    135. }
    136. else
    137. {
    138. parent->_right = cur->_left;
    139. }
    140. }
    141. delete cur;
    142. cur = nullptr;
    143. }
    144. else//左右都为空,叶子节点,这里采用用右树的最小节点进行删除
    145. {
    146. Node* minParent = cur;
    147. Node* min = cur->_right;//cur是要删除的节点
    148. while (min->_left)//寻找最小节点
    149. {
    150. minParent = min;
    151. min = min->_left;
    152. }
    153. swap(cur->_key, min->_key);
    154. if (minParent->_left == min)
    155. {
    156. minParent->_left = min->_right;
    157. }
    158. else
    159. minParent->_right = min->_right;
    160. delete min;
    161. }
    162. return true;
    163. }
    164. }
    165. return false;
    166. }
    167. bool FindR(const K& key)
    168. {
    169. return _FindR(_root, key);
    170. }
    171. bool InsertR(const K& key)
    172. {
    173. return _InsertR(_root, key);
    174. }
    175. bool EraseR(const K& key)
    176. {
    177. return _Eraser(_root, key);
    178. }
    179. ~BStree()
    180. {
    181. Destory(_root);
    182. }
    183. BStree()
    184. {}
    185. BStree(const BStree& t)
    186. {
    187. _root = _Copy(t._root);
    188. }
    189. BStree& operator=(BStree t)
    190. {
    191. swap(_root, t._root);
    192. return *this;
    193. }
    194. private:
    195. Node* _Copy(Node* root)
    196. {
    197. if (root == nullptr)
    198. {
    199. return nullptr;
    200. }
    201. Node* copyRoot = new Node(root->_key);
    202. copyRoot->_left = _Copy(root->_left);
    203. copyRoot->_right = _Copy(root->_right);
    204. return copyRoot;
    205. }
    206. void Destory(Node*& root)//采用引用可让root置空起作用
    207. {
    208. if (root == nullptr)
    209. return;
    210. Destory(root->_left);
    211. Destory(root->_right);
    212. delete root;
    213. root = nullptr;
    214. }
    215. bool _Eraser(Node*& root, const K& key)
    216. {
    217. if (root == nullptr)
    218. return false;
    219. if (root->_key < key)
    220. {
    221. return _Eraser(root->_right, key);
    222. }
    223. else if (root->_key > key)
    224. {
    225. return _Eraser(root->_left, key);
    226. }
    227. else
    228. {
    229. Node* del = root;
    230. if (root->_left == nullptr)
    231. {
    232. root = root->_right;
    233. }
    234. else if (root->_right == nullptr)
    235. {
    236. root = root->_left;//由于是引用,可直接这样将二叉树连接起来
    237. }
    238. else
    239. {
    240. //找右树的最左节点
    241. Node* min = root->_right;
    242. while (min->_left)
    243. {
    244. min = min->_left;
    245. }
    246. swap(root->_key, min->_key);
    247. return _Eraser(root->_right, key);
    248. }
    249. delete del;
    250. return true;
    251. }
    252. }
    253. bool _InsertR(Node*& root, const K& key)
    254. {
    255. if (root == nullptr)//根为空,直接插入
    256. {
    257. root = new Node(key);
    258. return true;
    259. }
    260. if (root->_key < key)
    261. {
    262. return _InsertR(root->_right, key);
    263. }
    264. else if (root->_key > key)
    265. {
    266. return _InsertR(root->_left, key);
    267. }
    268. else
    269. {
    270. return false;
    271. }
    272. }
    273. bool _FindR(Node *root,const K& key)
    274. {
    275. if (root == nullptr)
    276. return false;
    277. if (root->_key < key)
    278. {
    279. return _FindR(root->right, key);
    280. }
    281. else if (root->_key > key)
    282. {
    283. return _FindR(root->left, key);
    284. }
    285. else
    286. {
    287. return true;
    288. }
    289. }
    290. void _InOrder(Node *root)
    291. {
    292. if (root == nullptr)
    293. return;
    294. _InOrder(root->_left);
    295. cout << root->_key << " ";
    296. _InOrder(root->_right);
    297. }
    298. private:
    299. Node* _root = nullptr;
    300. };
    301. int main()
    302. {
    303. BStree<int> t;
    304. int a[] = { 1,1,2,2,3,6,165,132,4185,123 };
    305. for (auto e : a)
    306. {
    307. t.Insert(e);
    308. }
    309. BStree<int> copy = t;
    310. copy.InOrder();
    311. t.InOrder();
    312. BStree<int> t1;
    313. t1.Insert(2);
    314. t1.Insert(1);
    315. t1.Insert(3);
    316. copy = t1;
    317. copy.InOrder();
    318. cout << endl;
    319. t1.InOrder();
    320. cout << endl;
    321. return 0;
    322. }

     二叉搜索树的应用

     .K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
    的值。
    比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
    以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树

    在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误

    KV模型:每一个关键码key,都有与之对应的值Value,即的键值对。该种方
    式在现实生活中非常常见:
    比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
    文单词与其对应的中文就构成一种键值对;


    再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出
    现次数就是就构成一种键值对
    KV模型通过K去找V

    Key/Value模型 

    1. namespace KeyValue
    2. {
    3. template<class K, class V>
    4. struct BSTreeNode
    5. {
    6. BSTreeNode* _left;//Key和Value绑到一起
    7. BSTreeNode* _right;
    8. K _key;
    9. V _value;
    10. BSTreeNode(const K& key, const V& value)
    11. :_left(nullptr)
    12. , _right(nullptr)
    13. , _key(key)
    14. , _value(value)
    15. {}
    16. };
    17. template<class K, class V>
    18. class BSTree
    19. {
    20. typedef BSTreeNode 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)//查找的时候以K去查找,返回的时候返回节点指针,以便于修改
    60. {
    61. Node* cur = _root;
    62. while (cur)
    63. {
    64. if (cur->_key < key)
    65. {
    66. cur = cur->_right;
    67. }
    68. else if (cur->_key > key)
    69. {
    70. cur = cur->_left;
    71. }
    72. else
    73. {
    74. return cur;
    75. }
    76. }
    77. return nullptr;
    78. }
    79. bool Erase(const K& key)//用K删除
    80. {
    81. //...
    82. return true;
    83. }
    84. void InOrder()
    85. {
    86. _InOrder(_root);
    87. cout << endl;
    88. }
    89. private:
    90. void _InOrder(Node* root)
    91. {
    92. if (root == nullptr)
    93. {
    94. return;
    95. }
    96. _InOrder(root->_left);
    97. cout << root->_key << ":" << root->_value << endl;
    98. _InOrder(root->_right);
    99. }
    100. private:
    101. Node* _root = nullptr;
    102. };

    英译汉

     统计水果出现的次数

     链表相交和复杂链表的赋值可用kv模型。

  • 相关阅读:
    双周赛114(模拟、枚举 + 哈希、DFS)
    洛谷 P3966 [TJOI2013]单词(AC自动机, fail 树)
    数字化工厂系统有什么现实优势
    Aws EKS 技术文章
    如何减少爬虫产生的网络负载:爬取间隔和缓存控制策略
    【算法集训 | 暑期刷题营】7.28题---01背包问题
    分库分表实战:竿头日上—千万级数据优化之读写分离
    教你如何使用API接口获取数据!
    一种能够使身体吸收火气、水气和风气的电脑
    A类电能质量在线监测装置
  • 原文地址:https://blog.csdn.net/weixin_49449676/article/details/127480046