当前位置:   article > 正文

C++数据结构 —— 二叉搜索树_定义二叉查找树的数据结构,并使用递归算法实现对二叉查找树的查找算法。

定义二叉查找树的数据结构,并使用递归算法实现对二叉查找树的查找算法。

目录

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

1.1二叉搜索树的基本特征

 2.二叉搜索树的实现

2.1数据的插入(迭代实现)

2.2数据的搜索(迭代实现)

2.3中序遍历(递归实现)

2.4数据的删除(迭代实现)

2.5数据的搜索(递归实现)

2.6数据的插入(递归实现)

2.7数据的删除(递归实现)

2.8类的完善

3.二叉搜索树的应用

4.完整代码

 二叉搜索树

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

二叉搜索树又称二叉排序树,它可以是一颗空树。二叉搜索树的作用是搜索,排序(二叉搜索树的中序遍历是一组递增有序数据)。

1.1二叉搜索树的基本特征

如果某颗二叉树(包括空树)满足以下性质,可以作为一颗二叉搜索树:

        1.如果左子树不为空,其键值应小于根节点的键值。

        2.如果右子树不为空,其键值应大于根节点的键值。

        3.左右子树都满足上述条件。

没有二叉搜索树之前,常用的查找算法为二分查找。但是二分查找是有局限性的(必须针对有序数组)。二叉搜索树因其特性,例如我们需要查找Key值,只需要与根节点的键值做比较:若Key小于根节点的键值,则往根节点的左子树遍历;若Key值大于根节点的键值,则往根节点的右子树遍历。经计算,查找的次数等于二叉搜索树的深度。正因为如此,二叉搜索树并不是一个优秀的数据结构,因为一但碰到极端情况,二叉搜索树的搜索效率将会大打折扣。所以在往后的章节中,将会使其平衡。

 2.二叉搜索树的实现

 将二叉搜索树定义为一个类,现在将展示类的框架。往后所有的演示代码,都可以直接加入其中:

  1. // 节点
  2. template <class K>
  3. struct BST_node
  4. {
  5. BST_node<K>* _left; //左子树
  6. BST_node<K>* _right; //右子树
  7. K _key;
  8. BST_node(const K& _key)
  9. :_key(key),_left(nullptr),_right(nullptr)
  10. {}
  11. };
  12. template <class K> //节点键值的数据类型
  13. class BST
  14. {
  15. typedef BST_node<K> Node;
  16. public:
  17. private:
  18. Node* _root; //根节点
  19. };

2.1数据的插入(迭代实现)

  1. bool insert(const K& key)
  2. {
  3. if (_root == nullptr)
  4. {
  5. _root = new Node(key);
  6. return true;
  7. }
  8. Node* prev = nullptr; // cur的父节点
  9. Node* cur = _root;
  10. while (cur)
  11. {
  12. if (key < cur->_key) //如果比根节点的键值小
  13. {
  14. prev = cur;
  15. cur = cur->_left;
  16. }
  17. else if(key > cur->_key) //如果比根节点的键值大
  18. {
  19. prev = cur;
  20. cur = cur->_right;
  21. }
  22. else
  23. {
  24. // 我们不允许插入重复的数据
  25. return false;
  26. }
  27. }
  28. // 直到遍历到空,才施行插入
  29. cur = new Node(key);
  30. if (key < prev->_key)
  31. {
  32. prev->_left = cur;
  33. }
  34. else if (key > prev->_key)
  35. {
  36. prev->_right = cur;
  37. }
  38. return true;
  39. }

2.2数据的搜索(迭代实现)

  1. bool find(const K& key)
  2. {
  3. if (_root == nullptr)
  4. {
  5. return false;
  6. }
  7. Node* cur = _root;
  8. while (cur)
  9. {
  10. if (key < cur->_key)
  11. {
  12. cur = cur->_left;
  13. }
  14. else if (key > cur->_key)
  15. {
  16. cur = cur->_right;
  17. }
  18. else
  19. {
  20. // 找到了
  21. return true;
  22. }
  23. }
  24. return false;
  25. }

2.3中序遍历(递归实现)

  1. void MidTraval() //此接口作公有
  2. {
  3. __MidTraval(_root);
  4. cout << endl;
  5. }
  6. void __MidTraval(Node* root) //此接口做私有
  7. {
  8. if (root == nullptr)
  9. {
  10. return;
  11. }
  12. __MidTraval(root->_left);
  13. cout << root->_key << " ";
  14. __MidTraval(root->_right);
  15. }

2.4数据的删除(迭代实现)

需要注意,要删除二叉搜索树的节点,就必须分两种情况讨论:

        1.要删除节点的左子树或右子树为空。

        2.要删除节点的左、右子树都不为空。

  1. bool erase(const K& key)
  2. {
  3. if (_root == nullptr)
  4. {
  5. return false;
  6. }
  7. Node* prev = _root;
  8. Node* cur = _root;
  9. while (cur)
  10. {
  11. if (key < cur->_key)
  12. {
  13. prev = cur;
  14. cur = cur->_left;
  15. }
  16. else if (key > cur->_key)
  17. {
  18. prev = cur;
  19. cur = cur->_right;
  20. }
  21. else
  22. {
  23. // 如果左子树为空
  24. if (cur->_left == nullptr)
  25. {
  26. // 假设右子树不为空,则将右子树托孤给父节点
  27. if (_root == cur)
  28. {
  29. _root = _root->_right;
  30. }
  31. else if (prev->_left == cur)
  32. {
  33. prev->_left = cur->_right;
  34. }
  35. else if (prev->_right == cur)
  36. {
  37. prev->_right = cur->_right;
  38. }
  39. delete cur;
  40. return true;
  41. }
  42. // 如果右子树为空
  43. else if (cur->_right == nullptr)
  44. {
  45. //假设左子树不为空,则将左子树托孤给父节点
  46. if (_root == cur)
  47. {
  48. _root = _root->_left;
  49. }
  50. else if (prev->_left == cur)
  51. {
  52. prev->_left = cur->_left;
  53. }
  54. else if (prev->_right == cur)
  55. {
  56. prev->_right = cur->_left;
  57. }
  58. delete cur;
  59. return true;
  60. }
  61. // 如果左右子树都不为空
  62. else
  63. {
  64. // 假设使用右子树的最小值替代
  65. Node* prev = _root;
  66. Node* minRight = cur->_right;
  67. while (minRight->_left) //二叉树特性,越往左越小
  68. {
  69. prev = minRight;
  70. minRight = minRight->_left;
  71. }
  72. cur->_key = minRight->_key;
  73. // 替换好后,就要删除minRight
  74. if (prev->_left == minRight)
  75. {
  76. prev->_left = minRight->_right;
  77. }
  78. else if (prev->_right == minRight)
  79. {
  80. prev->_right = minRight->_right;
  81. }
  82. delete minRight;
  83. return true;
  84. }
  85. }
  86. }
  87. return false;
  88. }

2.5数据的搜索(递归实现)

  1. bool findR(const K& key)
  2. {
  3. return __findR(_root, key);
  4. }
  5. bool __findR(Node* root, const K& key) //此接口作私有
  6. {
  7. if (root == nullptr)
  8. {
  9. return false;
  10. }
  11. if (key < root->_key)
  12. {
  13. return __findR(root->_left, key);
  14. }
  15. else if (key > root->_key)
  16. {
  17. return __findR(root->_right, key);
  18. }
  19. return true;
  20. }

2.6数据的插入(递归实现)

  1. bool insertR(const K& key)
  2. {
  3. return __insertR(_root, key);
  4. }
  5. bool __insertR(Node*& root, const K& key) //此接口作私有
  6. {
  7. if (root == nullptr)
  8. {
  9. root = new Node(key); //注意引用传参,root相当于root->left或root->right的别名
  10. return true;
  11. }
  12. if (key < root->_key)
  13. {
  14. return __insertR(root->_left, key);
  15. }
  16. else if (key > root->_key)
  17. {
  18. return __insertR(root->_right, key);
  19. }
  20. return false;
  21. }

2.7数据的删除(递归实现)

  1. bool eraseR(const K& key)
  2. {
  3. return __eraseR(_root, key);
  4. }
  5. bool __eraseR(Node*& root, const K& key) //此接口作私有
  6. {
  7. if (root == nullptr)
  8. {
  9. return false;
  10. }
  11. if (key < root->_key)
  12. {
  13. return __eraseR(root->_left, key);
  14. }
  15. else if (key > root->_key)
  16. {
  17. return __eraseR(root->_right, key);
  18. }
  19. else
  20. {
  21. Node* del = root;
  22. if (root->_left == nullptr)
  23. {
  24. // 此时root就是要删除的节点,并且是root的父节点的子节点的引用(root == root->_left...)
  25. root = root->_right;
  26. delete del;
  27. return true;
  28. }
  29. else if (root->_right == nullptr)
  30. {
  31. root = root->_left;
  32. delete del;
  33. return true;
  34. }
  35. else
  36. {
  37. Node* prev = _root;
  38. Node* minRight = root->_right;
  39. while (minRight->_left) //二叉树特性,越往左越小
  40. {
  41. prev = minRight;
  42. minRight = minRight->_left;
  43. }
  44. root->_key = minRight->_key;
  45. // 替换好后,就要删除minRight
  46. if (prev->_left == minRight)
  47. {
  48. prev->_left = minRight->_right;
  49. }
  50. else if (prev->_right == minRight)
  51. {
  52. prev->_right = minRight->_right;
  53. }
  54. delete minRight;
  55. return true;
  56. }
  57. }
  58. return false;
  59. }

2.8类的完善

  1. BST()
  2. :_root(nullptr)
  3. {}
  4. ~BST()
  5. {
  6. Destructor(_root);
  7. _root = nullptr;
  8. }
  9. void Destructor(Node* root) //此函数作私有
  10. {
  11. if (root == nullptr)
  12. {
  13. return;
  14. }
  15. // 后序删除
  16. Destructor(root->_left);
  17. Destructor(root->_right);
  18. delete root;
  19. }
  20. BST(const BST<K>& t)
  21. {
  22. _root = Copy(t._root);
  23. }
  24. Node* Copy(Node* root) //此接口作私有
  25. {
  26. if (root == nullptr)
  27. {
  28. return nullptr;
  29. }
  30. Node* ret = new Node(root->_key);
  31. ret->_left = Copy(root->_left);
  32. ret->_right = Copy(root->_right);
  33. return ret;
  34. }
  35. BST<K>& operator==(BST<K> t) //现代写法
  36. {
  37. swap(_root, t._root);
  38. return *this;
  39. }

3.二叉搜索树的应用

1.K模型:

        K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。上面模拟实现的搜索就是K模型。

        例如将英文字典所有的英文单词存储二叉搜索树这个数据结构,那么可将英文单词看作关键码Key,假设我们想查找"hello"这个单词,直接去数据结构找即可。

2.KV模型:

        每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见。

        例如英汉互译,一个英文单词对应了多个汉语翻译。我们将<英文单词,中文翻译的数组>这样的键值对放入二叉搜索树中。例如查找"hello"这个单词的中文翻译,只需要查找键值对的英文单词即可。

KV模型例题:

给定下面数组,求每种水果出现的次数:

  1. string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
  2. "苹果", "香蕉", "苹果", "香蕉" };

第一步:先实现二叉搜索树(为了方便,这里只保留插入数据、查找和中序遍历的接口):

  1. namespace KV
  2. {
  3. // 节点
  4. template <class Key,class Val>
  5. struct BST_node
  6. {
  7. BST_node<Key,Val>* _left; //左子树
  8. BST_node<Key,Val>* _right; //右子树
  9. Key _key;
  10. Val _val;
  11. BST_node(const Key& key,const Val& val)
  12. :_key(key), _val(val),_left(nullptr), _right(nullptr)
  13. {}
  14. };
  15. template <class Key,class Val>
  16. class BST
  17. {
  18. typedef BST_node<Key,Val> Node;
  19. public:
  20. bool insert(const Key& key,const Val& val)
  21. {
  22. if (_root == nullptr)
  23. {
  24. _root = new Node(key,val);
  25. return true;
  26. }
  27. Node* prev = nullptr;
  28. Node* cur = _root;
  29. while (cur)
  30. {
  31. if (key < cur->_key)
  32. {
  33. prev = cur;
  34. cur = cur->_left;
  35. }
  36. else if (key > cur->_key)
  37. {
  38. prev = cur;
  39. cur = cur->_right;
  40. }
  41. else
  42. {
  43. return false;
  44. }
  45. }
  46. cur = new Node(key,val);
  47. if (key < prev->_key)
  48. {
  49. prev->_left = cur;
  50. }
  51. else if (key > prev->_key)
  52. {
  53. prev->_right = cur;
  54. }
  55. return true;
  56. }
  57. Node* find(const Key& key)
  58. {
  59. if (_root == nullptr)
  60. {
  61. return nullptr;
  62. }
  63. Node* cur = _root;
  64. while (cur)
  65. {
  66. if (key < cur->_key)
  67. {
  68. cur = cur->_left;
  69. }
  70. else if (key > cur->_key)
  71. {
  72. cur = cur->_right;
  73. }
  74. else
  75. {
  76. // 找到了
  77. return cur;
  78. }
  79. }
  80. return nullptr;
  81. }
  82. bool erase(const Key& key)
  83. {
  84. if (_root == nullptr)
  85. {
  86. return false;
  87. }
  88. Node* prev = _root;
  89. Node* cur = _root;
  90. while (cur)
  91. {
  92. if (key < cur->_key)
  93. {
  94. prev = cur;
  95. cur = cur->_left;
  96. }
  97. else if (key > cur->_key)
  98. {
  99. prev = cur;
  100. cur = cur->_right;
  101. }
  102. else
  103. {
  104. if (cur->_left == nullptr)
  105. {
  106. if (_root == cur)
  107. {
  108. _root = _root->_right;
  109. }
  110. else if (prev->_left == cur)
  111. {
  112. prev->_left = cur->_right;
  113. }
  114. else if (prev->_right == cur)
  115. {
  116. prev->_right = cur->_right;
  117. }
  118. delete cur;
  119. return true;
  120. }
  121. else if (cur->_right == nullptr)
  122. {
  123. if (_root == cur)
  124. {
  125. _root = _root->_left;
  126. }
  127. else if (prev->_left == cur)
  128. {
  129. prev->_left = cur->_left;
  130. }
  131. else if (prev->_right == cur)
  132. {
  133. prev->_right = cur->_left;
  134. }
  135. delete cur;
  136. return true;
  137. }
  138. else
  139. {
  140. Node* prev = _root;
  141. Node* minRight = cur->_right;
  142. while (minRight->_left)
  143. {
  144. prev = minRight;
  145. minRight = minRight->_left;
  146. }
  147. cur->_key = minRight->_key;
  148. if (prev->_left == minRight)
  149. {
  150. prev->_left = minRight->_right;
  151. }
  152. else if (prev->_right == minRight)
  153. {
  154. prev->_right = minRight->_right;
  155. }
  156. delete minRight;
  157. return true;
  158. }
  159. }
  160. }
  161. return false;
  162. }
  163. void MidTraval()
  164. {
  165. __MidTraval(_root);
  166. cout << endl;
  167. }
  168. private:
  169. Node* _root = nullptr;
  170. void __MidTraval(Node* root)
  171. {
  172. if (root == nullptr)
  173. {
  174. return;
  175. }
  176. __MidTraval(root->_left);
  177. cout << root->_key << ":" << root->_val << endl;
  178. __MidTraval(root->_right);
  179. }
  180. };
  181. }

第二步:算法实现:

  1. void test_count()
  2. {
  3. KV::BST<string, int> bt;
  4. string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
  5. "苹果", "香蕉", "苹果", "香蕉" };
  6. for (auto& e : arr)
  7. {
  8. KV::BST_node<string, int>* ret = bt.find(e);
  9. if (ret) //不为空,证明数据结构已有
  10. {
  11. ret->_val++; //次数++
  12. }
  13. else
  14. {
  15. bt.insert(e, 1);
  16. }
  17. }
  18. bt.MidTraval();
  19. }

4.完整代码

 二叉搜索树

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

闽ICP备14008679号