当前位置:   article > 正文

【数据结构】二叉搜索树_给定一串正整数,代表所要构建的二叉搜索树,计算并返回二叉搜索树的平均查找长度结

给定一串正整数,代表所要构建的二叉搜索树,计算并返回二叉搜索树的平均查找长度结

        二叉搜索树又称二叉排序树,是一种key搜索模型,它或是一棵空树,亦或是具有以下性质的二叉树:

  • 若根的左子树不为空,则左子树上所有节点的值都小于根节点的值;
  • 若根的右子树不为空,则右子树上所有节点的值都大于根节点的值;
  • 根的左右子树也分别为二叉搜索树。

        由以下数据构建出一棵二叉搜索树:

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

         将得到形如下图的一棵二叉搜索树:

        若中序遍历输出这棵二叉搜索树,将得到这样一个序列:

    1    3    4    6    7    8    10    13    14

        而这个序列恰好是升序的。这也是二叉搜索树的意义所在,对于一个有序数组来说,插入和删除的效率低(因为它是基于二分查找实现的),而一棵二叉搜索树的查找、排序、插入和删除效率较优良。

        本篇博客将通过C++来模拟实现二叉搜索树的重要功能,旨在让读者更好地理解它的功能及其原理。

目录

•  二叉搜索树的模拟实现

0 - 创建一棵二叉搜索树

1 - 查找

2 - 插入

3 - 删除

4 - 拷贝

5 - 销毁

6 - 完整代码

补、两种搜索模型

补、二叉搜索树的性能问题

补、二叉搜索树的相关OJ


 二叉搜索树的模拟实现

0 - 创建一棵二叉搜索树

  1. //树的节点
  2. template<class K>
  3. struct BSTreeNode
  4. {
  5. BSTreeNode<K>* _left; //左子树
  6. BSTreeNode<K>* _right; //右子树
  7. K _key; //节点的值
  8. BSTreeNode(const K& key) //通过初始化列表来构造一个树节点
  9. :_left(nullptr)
  10. , _right(nullptr)
  11. , _key(key)
  12. {}
  13. };
  14. //二叉搜索树
  15. template<class K>
  16. class BSTree
  17. {
  18. typedef BSTreeNode<K> Node;
  19. public:
  20. BSTree() //通过初始化列表来构造一棵二叉搜索树
  21. :_root(nullptr)
  22. {}
  23. BSTree<K>& operator=(BSTree<K> t) //赋值重置
  24. {
  25. swap(_root, t._root);
  26. return *this;
  27. }
  28. private:
  29. Node* _root;
  30. };

1 - 查找

        从根开始,将目标值与节点挨个比较查找,节点的值比根大就去根的右子树里查找,比根小就去左子树里查找。如果走到了空节点也还没找到,就说明这个值在树中不存在。这种方式最多查找树的高度次。

  1. bool Find(const K& key)
  2. {
  3. Node* cur = _root; //从根节点(当前节点)开始找
  4. while (cur) //节点不为空就继续找
  5. {
  6. if (cur->_key < key) //比根(当前节点)大走右子树
  7. {
  8. cur = cur->_right;
  9. }
  10. else if (cur->_key > key) //比根(当前节点)小走左子树
  11. {
  12. cur = cur->_left;
  13. }
  14. else
  15. {
  16. return true; //找到了就返回true
  17. }
  18. }
  19. return false;
  20. }

         也可以通过递归的方式来实现查找。

  1. //ps:在类内部写递归,需要先对递归进行一层封装
  2. //public:
  3. bool FindR(const K& key)
  4. {
  5. return _FindR(_root, key);
  6. }
  7. //private:
  8. bool _FindR(Node* root, const K& key)
  9. {
  10. if (root == nullptr)
  11. return false;
  12. if (root->_key < key)
  13. {
  14. return _FindR(root->_right, key);
  15. }
  16. else if (root->_key > key)
  17. {
  18. return _FindR(root->_left, key);
  19. }
  20. else
  21. {
  22. return true;
  23. }
  24. }

2 - 插入

        利用插入的值创建一个新的树节点。树为空,就直接将新节点赋给根节点的指针;树不为空,就按二叉搜索树的性质查找到合适的插入位置,在合适位置插入新节点。

  1. bool Insert(const K& key)
  2. {
  3. //树为空,就直接将新节点赋给根节点的指针
  4. if (_root == nullptr)
  5. {
  6. _root = new Node(key);
  7. return true;
  8. }
  9. //树不为空,就按二叉搜索树的性质查找到合适的插入位置
  10. Node* parent = nullptr; //父节点,用于记录当前节点的位置
  11. Node* cur = _root; //子节点,用于记录下一个要查找的节点
  12. while (cur)
  13. {
  14. if (cur->_key < key) //比根大就去右子树找
  15. {
  16. parent = cur; //将子节点更新为新的父节点
  17. cur = cur->_right; //子节点向右子树走
  18. }
  19. else if (cur->_key > key) //比根小就去左子树找
  20. {
  21. parent = cur; //将子节点更新为新的父节点
  22. cur = cur->_left; //子节点向左子树走
  23. }
  24. else
  25. {
  26. return false; //要插入的值已在树中就不插入了。在二叉搜索树中,每个值是不重复的
  27. }
  28. }
  29. //最后,父节点记录了合适的插入位置
  30. //在合适位置插入新节点
  31. cur = new Node(key);
  32. if (parent->_key < key)
  33. {
  34. parent->_right = cur; //比合适位置的值大就插入在右边
  35. }
  36. else
  37. {
  38. parent->_left = cur; //比合适位置的值小就插入在左边
  39. }
  40. return true;
  41. }

         同样,也可以通过递归的方式来实现插入。

  1. //public:
  2. bool InsertR(const K& key)
  3. {
  4. return _InsertR(_root, key);
  5. }
  6. //private:
  7. bool _InsertR(Node*& root, const K& key)
  8. {
  9. if (root == nullptr)
  10. {
  11. root = new Node(key);
  12. return true;
  13. }
  14. if (root->_key < key)
  15. {
  16. return _InsertR(root->_right, key);
  17. }
  18. else if (root->_key > key)
  19. {
  20. return _InsertR(root->_left, key);
  21. }
  22. else
  23. {
  24. return false;
  25. }
  26. }

3 - 删除

        首先查找验证元素是否在树中,若不存在,则返回false;若存在,就删除目标节点。

删除节点的确容易,直接释放节点即可,但删除的同时还应维护二叉搜索树原本的结构特性,于是,要删除的节点存在树中时,删除的处理就可能存在以下情况:

  1. 要删除的节点没有孩子节点;
  2. 要删除的节点只有左孩子;
  3. 要删除的节点只有右孩子;
  4. 要删除的节点既有左孩子又有右孩子。

        而编写时,情况1其实可以与情况2或者情况3合并,故节点删除的真正过程为:

  1. 若要删除的节点没有左孩子,就将它的右孩子给父节点,然后将它直接删除;
  2. 若要删除的节点没有右孩子,就将它的左孩子给父节点,然后将它直接删除;
  3. 若要删除的节点既有左孩子又有右孩子,就先用替换法做删除处理,然后再将它删除。

【Tips】替换法:用要删除节点的左子树的最大节点或右子树的最小节点(即左子树的最右节点或右子树的最左节点)替换要删除的节点。要删除的节点的左子树的最大节点或右子树的最小节点,它们的值是最接近要删除节点的,用它们中任意一个代替要删除的节点,不会破坏二叉搜索树原本的结构特性。所以,找不论左子树的最右节点,还是右子树的最左节点来替代要删除节点,均可。

  1. bool Erase(const K& key)
  2. {
  3. Node* parent = nullptr; //父节点/当前节点
  4. Node* cur = _root; //子节点/当前节点
  5. //先找到要删除的节点
  6. while (cur)
  7. {
  8. if (cur->_key < key)
  9. {
  10. parent = cur;
  11. cur = cur->_right;
  12. }
  13. else if (cur->_key > key)
  14. {
  15. parent = cur;
  16. cur = cur->_left;
  17. }
  18. else // 找到了要删除的节点,此时parent储存了要删除节点的父节点,cur储存了要删除的节点
  19. {
  20. // 要删除的节点没有左孩子
  21. if (cur->_left == nullptr)
  22. {
  23. //要删除的节点为根节点
  24. if (cur == _root)
  25. {
  26. _root = cur->_right;//因为cur没有左孩子,所以将cur的右孩子置为新的根节点
  27. }
  28. //不为根节点
  29. else
  30. {
  31. //如果cur是parent的右孩子,就把cur的右孩子置为parent新的右孩子(因为cur没有左孩子)
  32. if (parent->_right == cur)
  33. {
  34. parent->_right = cur->_right;
  35. }
  36. //但如果cur是parent的左孩子,就把cur的右孩子置为parent新的左孩子
  37. else
  38. {
  39. parent->_left = cur->_right;
  40. }
  41. }
  42. }
  43. // 要删除的节点没有右孩子
  44. else if (cur->_right == nullptr)
  45. {
  46. //当前节点为根节点
  47. if (cur == _root)
  48. {
  49. _root = cur->_left;//因为cur没有右孩子,所以将cur的左孩子置为新的根节点
  50. }
  51. //不为根节点
  52. else
  53. {
  54. //如果cur是parent的右孩子,就把cur的左孩子置为parent新的右孩子(因为cur没有右孩子)
  55. if (parent->_right == cur)
  56. {
  57. parent->_right = cur->_left;
  58. }
  59. //但如果cur是parent的左孩子,就把cur的右孩子置为parent新的左孩子
  60. else
  61. {
  62. parent->_left = cur->_left;
  63. }
  64. }
  65. }
  66. // 要删除的节点既有左孩子又有右孩子
  67. else
  68. {
  69. //那就先找到替代节点(即左子树的最右节点或右子树的最左节点)
  70. parent = cur; //更新parent为要删除的节点
  71. Node* leftMax = cur->_left;//定义leftMax初始为cur的左孩子
  72. //ps:要删除节点的左子树的最右节点或右子树的最左节点,它们的值是最接近要删除节点的,用它们中任意一个代替要删除的节点,不会破坏二叉搜索树原本的结构特性
  73. //所以,找不论左子树的最右节点,还是右子树的最左节点来替代要删除节点,均可
  74. //这里是找左子树的最右节点
  75. while (leftMax->_right)
  76. {
  77. parent = leftMax;
  78. leftMax = leftMax->_right;
  79. }
  80. //找到后,交换替代节点和要删除节点的值
  81. swap(cur->_key, leftMax->_key);
  82. //然后,做节点的删除处理,
  83. //经过上面找左子树的最右节点的代码逻辑,parent当前是leftMax的父节点
  84. //如果leftMax是parent的左孩子,就把leftMax的左孩子置为parent新的左孩子
  85. //如果leftMax是parent的右孩子,就把leftMax的左孩子置为parent新的右孩子
  86. //ps:leftMax已经是左子树的最右节点了,理论上它不会再有右孩子,但可能有左孩子。就算没有左孩子(即leftMax->_left==NULL),也不妨碍下面的代码重置parent的新孩子
  87. if (parent->_left == leftMax)
  88. {
  89. parent->_left = leftMax->_left;
  90. }
  91. else
  92. {
  93. parent->_right = leftMax->_left;
  94. }
  95. //然后让cur去指向替代节点
  96. cur = leftMax;
  97. }
  98. //做完删除的处理后,最终释放cur
  99. delete cur;
  100. return true;
  101. }
  102. }
  103. //树为空或树中没有要删除的值,就返回false
  104. return false;
  105. }

       同样,也可以通过递归的方式来实现删除。 

  1. //public:
  2. bool EraseR(const K& key)
  3. {
  4. return _EraseR(_root, key);
  5. }
  6. //private:
  7. bool _EraseR(Node*& root, const K& key)
  8. {
  9. if (root == nullptr)
  10. return false;
  11. if (root->_key < key)
  12. {
  13. return _EraseR(root->_right, key);
  14. }
  15. else if (root->_key > key)
  16. {
  17. return _EraseR(root->_left, key);
  18. }
  19. else
  20. {
  21. Node* del = root;
  22. //左为空
  23. if (root->_left == nullptr)
  24. {
  25. root = root->_right;
  26. }
  27. //右为空
  28. else if (root->_right == nullptr)
  29. {
  30. root = root->_left;
  31. }
  32. //左右不空
  33. else
  34. {
  35. Node* leftMax = root->_left;
  36. while (leftMax->_right)
  37. {
  38. leftMax = leftMax->_right;
  39. }
  40. swap(root->_key, leftMax->_key);
  41. return _EraseR(root->_left, key);
  42. }
  43. delete del;
  44. return true;
  45. }
  46. }

4 - 拷贝

        将一棵二叉搜索树拷贝一份给另一棵二叉搜索树,不仅仅是将树中的值进行拷贝,还要将树的结构(即节点指针)拷贝下来,所以这个过程应是深拷贝。

        可以通过递归结合前序遍历的方式来实现这个深拷贝,并且可以通过调用拷贝构造来进行深拷贝。

  1. //public:
  2. BSTree(const BSTree<K>& t)
  3. {
  4. _root = Copy(t._root);
  5. }
  6. //private:
  7. Node* Copy(Node* root)
  8. {
  9. if (root == nullptr)
  10. return nullptr;
  11. Node* copyroot = new Node(root->_key);
  12. copyroot->_left = Copy(root->_left);
  13. copyroot->_right = Copy(root->_right);
  14. return copyroot;
  15. }

5 - 销毁

        销毁一棵二叉搜索树,需要将其所有的节点释放。这个过程可以通过递归结合后序遍历的方式来实现,并且可以通过调用析构来销毁一棵二叉搜索树。

  1. //public:
  2. ~BSTree()
  3. {
  4. Destroy(_root);
  5. }
  6. //private:
  7. void Destroy(Node*& root)
  8. {
  9. if (root == nullptr)
  10. return;
  11. Destroy(root->_left);
  12. Destroy(root->_right);
  13. delete root;
  14. root = nullptr;
  15. }

6 - 完整代码

  1. //搜索二叉树 - 根的左边比根小,右边比根大
  2. //key搜索模型
  3. namespace key
  4. {
  5. template<class K>
  6. struct BSTreeNode
  7. {
  8. BSTreeNode<K>* _left;
  9. BSTreeNode<K>* _right;
  10. K _key;
  11. BSTreeNode(const K& key)
  12. :_left(nullptr)
  13. , _right(nullptr)
  14. , _key(key)
  15. {}
  16. };
  17. template<class K>
  18. class BSTree
  19. {
  20. typedef BSTreeNode<K> Node;
  21. public:
  22. BSTree()
  23. :_root(nullptr)
  24. {}
  25. ~BSTree()
  26. {
  27. Destroy(_root);
  28. }
  29. BSTree(const BSTree<K>& t)
  30. {
  31. _root = Copy(t._root);
  32. }
  33. BSTree<K>& operator=(BSTree<K> t)
  34. {
  35. swap(_root, t._root);
  36. return *this;
  37. }
  38. //bool Insert(const K& key)
  39. //{
  40. // if (_root == nullptr)
  41. // {
  42. // _root = new Node(key);
  43. // return true;
  44. // }
  45. // Node* parent = nullptr;
  46. // Node* cur = _root;
  47. // while (cur)
  48. // {
  49. // if (cur->_key < key)
  50. // {
  51. // parent = cur;
  52. // cur = cur->_right;
  53. // }
  54. // else if (cur->_key > key)
  55. // {
  56. // parent = cur;
  57. // cur = cur->_left;
  58. // }
  59. // else
  60. // {
  61. // return false;
  62. // }
  63. // }
  64. // cur = new Node(key);
  65. // if (parent->_key < key)
  66. // {
  67. // parent->_right = cur;
  68. // }
  69. // else
  70. // {
  71. // parent->_left = cur;
  72. // }
  73. // return true;
  74. //}
  75. bool InsertR(const K& key)
  76. {
  77. return _InsertR(_root, key);
  78. }
  79. void InOrder()
  80. {
  81. _InOrder(_root);
  82. cout << endl;
  83. }
  84. /*bool Find(const K& key)
  85. {
  86. Node* cur = _root;
  87. while (cur)
  88. {
  89. if (cur->_key < key)
  90. {
  91. cur = cur->_right;
  92. }
  93. else if (cur->_key > key)
  94. {
  95. cur = cur->_left;
  96. }
  97. else
  98. {
  99. return true;
  100. }
  101. }
  102. return false;
  103. }*/
  104. bool FindR(const K& key)
  105. {
  106. return _FindR(_root, key);
  107. }
  108. //bool Erase(const K& key)
  109. //{
  110. // //替换法:左子树的最大节点或右子树的最小节点(即左子树的最右节点或右子树的最左节点)
  111. // Node* parent = nullptr;
  112. // Node* cur = _root;
  113. // //先找key
  114. // while (cur)
  115. // {
  116. // if (cur->_key < key)
  117. // {
  118. // parent = cur;
  119. // cur = cur->_right;
  120. // }
  121. // else if (cur->_key > key)
  122. // {
  123. // parent = cur;
  124. // cur = cur->_left;
  125. // }
  126. // else // 找到了
  127. // {
  128. // // 左为空
  129. // if (cur->_left == nullptr)
  130. // {
  131. // if (cur == _root)
  132. // {
  133. // _root = cur->_right;
  134. // }
  135. // else
  136. // {
  137. // if (parent->_right == cur)
  138. // {
  139. // parent->_right = cur->_right;
  140. // }
  141. // else
  142. // {
  143. // parent->_left = cur->_right;
  144. // }
  145. // }
  146. // }// 右为空
  147. // else if (cur->_right == nullptr)
  148. // {
  149. // if (cur == _root)
  150. // {
  151. // _root = cur->_left;
  152. // }
  153. // else
  154. // {
  155. // if (parent->_right == cur)
  156. // {
  157. // parent->_right = cur->_left;
  158. // }
  159. // else
  160. // {
  161. // parent->_left = cur->_left;
  162. // }
  163. // }
  164. // } // 左右都不为空
  165. // else
  166. // {
  167. // //找替代节点
  168. //
  169. // Node* parent = cur;
  170. // Node* leftMax = cur->_left;
  171. // while (leftMax->_right)
  172. // {
  173. // parent = leftMax;
  174. // leftMax = leftMax->_right;
  175. // }
  176. // swap(cur->_key, leftMax->_key);
  177. //
  178. // if (parent->_left == leftMax)
  179. // {
  180. // parent->_left = leftMax->_left;
  181. // }
  182. // else
  183. // {
  184. // parent->_right = leftMax->_left;
  185. // }
  186. // cur = leftMax;
  187. //
  188. // }
  189. // delete cur;
  190. // return true;
  191. // }
  192. // }
  193. //
  194. // return false;
  195. //}
  196. bool EraseR(const K& key)
  197. {
  198. return _EraseR(_root, key);
  199. }
  200. private:
  201. Node* Copy(Node* root)
  202. {
  203. if (root == nullptr)
  204. return nullptr;
  205. Node* copyroot = new Node(root->_key);
  206. copyroot->_left = Copy(root->_left);
  207. copyroot->_right = Copy(root->_right);
  208. return copyroot;
  209. }
  210. void Destroy(Node*& root)
  211. {
  212. if (root == nullptr)
  213. return;
  214. Destroy(root->_left);
  215. Destroy(root->_right);
  216. delete root;
  217. root = nullptr;
  218. }
  219. bool _InsertR(Node*& root, const K& key)
  220. {
  221. if (root == nullptr)
  222. {
  223. root = new Node(key);
  224. return true;
  225. }
  226. if (root->_key < key)
  227. {
  228. return _InsertR(root->_right, key);
  229. }
  230. else if (root->_key > key)
  231. {
  232. return _InsertR(root->_left, key);
  233. }
  234. else
  235. {
  236. return false;
  237. }
  238. }
  239. void _InOrder(Node* root)
  240. {
  241. if (root == NULL)
  242. {
  243. return;
  244. }
  245. _InOrder(root->_left);
  246. cout << root->_key << " ";
  247. _InOrder(root->_right);
  248. }
  249. bool _FindR(Node* root, const K& key)
  250. {
  251. if (root == nullptr)
  252. return false;
  253. if (root->_key < key)
  254. {
  255. return _FindR(root->_right, key);
  256. }
  257. else if (root->_key > key)
  258. {
  259. return _FindR(root->_left, key);
  260. }
  261. else
  262. {
  263. return true;
  264. }
  265. }
  266. bool _EraseR(Node*& root, const K& key)
  267. {
  268. if (root == nullptr)
  269. return false;
  270. if (root->_key < key)
  271. {
  272. return _EraseR(root->_right, key);
  273. }
  274. else if (root->_key > key)
  275. {
  276. return _EraseR(root->_left, key);
  277. }
  278. else
  279. {
  280. Node* del = root;
  281. //左为空
  282. if (root->_left == nullptr)
  283. {
  284. root = root->_right;
  285. }
  286. //右为空
  287. else if (root->_right == nullptr)
  288. {
  289. root = root->_left;
  290. }
  291. //左右不空
  292. else
  293. {
  294. Node* leftMax = root->_left;
  295. while (leftMax->_right)
  296. {
  297. leftMax = leftMax->_right;
  298. }
  299. swap(root->_key, leftMax->_key);
  300. return _EraseR(root->_left, key);
  301. }
  302. delete del;
  303. return true;
  304. }
  305. }
  306. private:
  307. Node* _root;
  308. };
  309. void TestBSTree1()
  310. {
  311. int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
  312. BSTree<int> t;
  313. for (auto e : a)
  314. {
  315. t.InsertR(e);
  316. }
  317. t.InOrder();
  318. t.EraseR(4);
  319. t.InOrder();
  320. t.EraseR(6);
  321. t.InOrder();
  322. t.EraseR(7);
  323. t.InOrder();
  324. t.EraseR(3);
  325. t.InOrder();
  326. for (auto e : a)
  327. {
  328. t.EraseR(e);
  329. }
  330. t.InOrder();
  331. }
  332. void TestBSTree2()
  333. {
  334. int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
  335. BSTree<int> t;
  336. for (auto e : a)
  337. {
  338. t.InsertR(e);
  339. }
  340. BSTree<int> t1(t);
  341. t.InOrder();
  342. t1.InOrder();
  343. }
  344. }

补、两种搜索模型

• K模型

        K模型是一种只有key作为关键码(即需要搜索到的值),结构中只需要存储key的搜索模型,其模型本质是“在不在”。

        模型的应用情景例如,输入法自动检查在键盘上输入的一个单词的拼写是否正确,其方式之一是将输入的单词与词库中的单词进行匹配,若匹配成功,则说明拼写正确,若在词库中匹配不到,则说明拼写错误。

       最普通的二叉搜索树就是一个K模型,即每个树节点只存储一个数据,树节点的实际意义就是所存储的数据的实际意义。

• KV模型

        在KV模型中,每一个关键码key都有与之一一对应的值value,也就是说,模型中所存的其实是<key,value>的键值对。其模型本质是key和value的映射关系。

        模型的应用情景例如,在英汉词典中,英文与中文的对应关系,通过英文单词可以快速找到与之对应的中文,英文单词与其对应的中文就构成一种<英文,中文>的键值对。

        二叉搜索树也可以改造为一个KV模型,即每个树节点存储了一个<key,value>的键值对,树节点的实际意义更加丰富多元。

  1. //key-value搜索模型
  2. namespace key_value
  3. {
  4. template<class K, class V>
  5. struct BSTreeNode
  6. {
  7. BSTreeNode<K, V>* _left;
  8. BSTreeNode<K, V>* _right;
  9. K _key;
  10. V _value;
  11. BSTreeNode(const K& key, const V& value)
  12. :_left(nullptr)
  13. , _right(nullptr)
  14. , _key(key)
  15. , _value(value)
  16. {}
  17. };
  18. template<class K, class V>
  19. class BSTree
  20. {
  21. typedef BSTreeNode<K, V> Node;
  22. public:
  23. BSTree()
  24. :_root(nullptr)
  25. {}
  26. void InOrder()
  27. {
  28. _InOrder(_root);
  29. cout << endl;
  30. }
  31. Node* FindR(const K& key)
  32. {
  33. return _FindR(_root, key);
  34. }
  35. bool InsertR(const K& key, const V& value)
  36. {
  37. return _InsertR(_root, key, value);
  38. }
  39. bool EraseR(const K& key)
  40. {
  41. return _EraseR(_root, key);
  42. }
  43. private:
  44. bool _EraseR(Node*& root, const K& key)
  45. {
  46. if (root == nullptr)
  47. return false;
  48. if (root->_key < key)
  49. {
  50. return _EraseR(root->_right, key);
  51. }
  52. else if (root->_key > key)
  53. {
  54. return _EraseR(root->_left, key);
  55. }
  56. else
  57. {
  58. Node* del = root;
  59. // 1、左为空
  60. // 2、右为空
  61. // 3、左右都不为空
  62. if (root->_left == nullptr)
  63. {
  64. root = root->_right;
  65. }
  66. else if (root->_right == nullptr)
  67. {
  68. root = root->_left;
  69. }
  70. else
  71. {
  72. Node* leftMax = root->_left;
  73. while (leftMax->_right)
  74. {
  75. leftMax = leftMax->_right;
  76. }
  77. swap(root->_key, leftMax->_key);
  78. return _EraseR(root->_left, key);
  79. }
  80. delete del;
  81. return true;
  82. }
  83. }
  84. bool _InsertR(Node*& root, const K& key, const V& value)
  85. {
  86. if (root == nullptr)
  87. {
  88. root = new Node(key, value);
  89. return true;
  90. }
  91. if (root->_key < key)
  92. {
  93. return _InsertR(root->_right, key, value);
  94. }
  95. else if (root->_key > key)
  96. {
  97. return _InsertR(root->_left, key, value);
  98. }
  99. else
  100. {
  101. return false;
  102. }
  103. }
  104. Node* _FindR(Node* root, const K& key)
  105. {
  106. if (root == nullptr)
  107. return nullptr;
  108. if (root->_key < key)
  109. {
  110. return _FindR(root->_right, key);
  111. }
  112. else if (root->_key > key)
  113. {
  114. return _FindR(root->_left, key);
  115. }
  116. else
  117. {
  118. return root;
  119. }
  120. }
  121. void _InOrder(Node* root)
  122. {
  123. if (root == NULL)
  124. {
  125. return;
  126. }
  127. _InOrder(root->_left);
  128. cout << root->_key << ":" << root->_value << endl;
  129. _InOrder(root->_right);
  130. }
  131. private:
  132. Node* _root;
  133. };
  134. void TestBSTree1()
  135. {
  136. //BSTree<string, Date> carTree;
  137. BSTree<string, string> dict;
  138. dict.InsertR("insert", "插入");
  139. dict.InsertR("sort", "排序");
  140. dict.InsertR("right", "右边");
  141. dict.InsertR("date", "日期");
  142. string str;
  143. while (cin >> str)
  144. {
  145. BSTreeNode<string, string>* ret = dict.FindR(str);
  146. if (ret)
  147. {
  148. cout << ret->_value << endl;
  149. }
  150. else
  151. {
  152. cout << "无此单词" << endl;
  153. }
  154. }
  155. }
  156. void TestBSTree2()
  157. {
  158. // 统计水果出现的次数
  159. string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
  160. BSTree<string, int> countTree;
  161. for (auto& str : arr)
  162. {
  163. auto ret = countTree.FindR(str);
  164. if (ret == nullptr)
  165. {
  166. countTree.InsertR(str, 1);
  167. }
  168. else
  169. {
  170. ret->_value++;
  171. }
  172. }
  173. countTree.InOrder();
  174. }
  175. }

补、二叉搜索树的性能问题

        二叉搜索树中涉及树节点的的主要操作都必须做查找,所以查找的效率就基本代表了二叉搜索树中各操作的性能。

        设一棵有n个节点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度即为节点在二叉搜索树的深度的函数,节点越深,查找得就越久。 而对于同一个关键码key的集合,如果每个关键码key插入树的次序不同,就可能得到不同结构的二叉搜索树:

        最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为logN。但是最坏情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:N^2。所以,当二叉搜索树退化成单支树,它就失去了原本的性能优势。

        那么,是否存在一种可能,不论以什么次序插入关键码,都使二叉搜索树的性能达到最优?这个问题可以由另外两种树结构——AVL树(详见【数据结构】平衡树之AVL树)和红黑树(详见【数据结构】平衡树之红黑树)来解决。

补、二叉搜索树的相关OJ

根据二叉树创建字符串

  1. class Solution {
  2. public:
  3. string tree2str(TreeNode* root) {
  4. if (root == nullptr)
  5. return "";
  6. string str = to_string(root->val);
  7. if (root->left || root->right)
  8. {
  9. str += '(';
  10. str += tree2str(root->left);
  11. str += ')';
  12. }
  13. if (root->right)
  14. {
  15. str += '(';
  16. str += tree2str(root->right);
  17. str += ')';
  18. }
  19. return str;
  20. }
  21. };

二叉树的最近公共祖先

  1. // 法1.搜索二叉树倒着走-> 链表相交
  2. // a、三叉链 - o(N)
  3. // b、普通树 - o(N)
  4. // 法2.一个在左一个在右,此个即公共祖先
  5. // a、普通树暴力求解 - o(N^2)
  6. // b、搜索二叉树 - o(N)
  7. //
  8. // 搜索二叉树
  9. class Solution {
  10. public:
  11. bool Find(TreeNode* tree, TreeNode* x)
  12. {
  13. if (tree == nullptr)
  14. return false;
  15. return tree == x
  16. || Find(tree->left, x)
  17. || Find(tree->right, x);
  18. }
  19. TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
  20. if (root == nullptr)
  21. return nullptr;
  22. if (root == p || root == q)
  23. return root;
  24. bool pInLeft, pInRight, qInLeft, qInRight;
  25. pInLeft = Find(root->left, p);
  26. pInRight = !pInLeft;
  27. qInLeft = Find(root->left, q);
  28. qInRight = !qInLeft;
  29. if (pInLeft && qInLeft)
  30. {
  31. return lowestCommonAncestor(root->left, p, q);
  32. }
  33. else if (pInRight && qInRight)
  34. {
  35. return lowestCommonAncestor(root->right, p, q);
  36. }
  37. else
  38. {
  39. return root;
  40. }
  41. }
  42. };
  1. //前序遍历,非所要数据则分别入栈,遇到所要数据则停止入栈
  2. //最终比较两个栈的数据,不相同则删除栈顶,相同即公共祖先
  3. class Solution {
  4. bool FindPath(TreeNode* root, TreeNode* x, stack<TreeNode*>& path)
  5. {
  6. if (root == nullptr)
  7. {
  8. return false;
  9. }
  10. path.push(root);
  11. if (root == x)
  12. {
  13. return true;
  14. }
  15. if (FindPath(root->left, x, path))
  16. return true;
  17. if (FindPath(root->right, x, path))
  18. return true;
  19. path.pop();
  20. return false;
  21. }
  22. TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
  23. stack<TreeNode*> pPath,qPath;
  24. FindPath(root, p, pPath);
  25. FindPath(root, q, qPath);
  26. while (pPath.size() > qPath.size())
  27. {
  28. pPath.pop();
  29. }
  30. while (pPath.size() < qPath.size())
  31. {
  32. qPath.pop();
  33. }
  34. while (pPath.top() != qPath.top())
  35. {
  36. pPath.pop();
  37. qPath.pop();
  38. }
  39. return pPath.top();
  40. }
  41. };

二叉搜索树与双向链表

  1. //左路节点->左路节点的右子树->左路节点
  2. //cur指向一个节点意味着访问另一棵树的开始
  3. //栈里面的节点意味着压迫访问它的右子树
  4. class Solution {
  5. public:
  6. void InOrder(TreeNode* cur,TreeNode*& prev)
  7. {
  8. if (cur == nullptr)
  9. return;
  10. InOrder(cur->left,prev);
  11. //当前left指向前一个节点
  12. cur->left = prev;
  13. //前一个right指向当前节点
  14. if(prev)
  15. prev->right = cur;
  16. prev = cur;
  17. InOrder(cur->right,prev);
  18. }
  19. TreeNode* Convert(TreeNode* pRootOfTree) {
  20. TreeNode* prev = nullptr;
  21. InOrder(pRootOfTree, prev);
  22. TreeNode* head = pRootOfTree;
  23. while (head && head->left)
  24. {
  25. head = head->left;
  26. }
  27. return head;
  28. }
  29. };

从前序与中序遍历序列构造二叉树

  1. class Solution {
  2. public:
  3. TreeNode* _build(vector<int>& preorder, vector<int>& inorder,
  4. int& prei, int inbegin, int inend)
  5. {
  6. if (inbegin > inend)
  7. return nullptr;
  8. TreeNode* root = new TreeNode(preorder[prei]);
  9. int rooti = inbegin;
  10. while (rooti <= inend)
  11. {
  12. if (preorder[prei] == inorder[rooti])
  13. break;
  14. ++rooti;
  15. }
  16. //[inbegin,rooti-1] rooti [rooti+1,inend]
  17. ++prei;
  18. root->left = _build(preorder, inorder, prei, inbegin, rooti - 1);
  19. root->right= _build(preorder, inorder, prei, rooti + 1,inend);
  20. return root;
  21. }
  22. TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
  23. int i = 0;
  24. return _build(preorder,inorder, i, 0, inorder.size() - 1);
  25. }
  26. };

二叉树的前序遍历,非递归迭代实现

  1. class Solution {
  2. public:
  3. vector<int> preorderTraversal(TreeNode* root) {
  4. stack<TreeNode*> st;
  5. TreeNode* cur = root;
  6. vector<int> v;
  7. //访问一棵树的开始
  8. //1.访问左路节点,左路节点入栈,后续依次访问左路节点的右子树
  9. while (cur || !st.empty())
  10. {
  11. while(cur)
  12. {
  13. v.push_back(cur->val);
  14. st.push(cur);
  15. cur = cur->left;
  16. }
  17. //依次访问左路节点的右子树
  18. TreeNode* top = st.top();
  19. st.pop();
  20. //子问题的方式访问右子树
  21. cur = top->right;
  22. }
  23. return v;
  24. }
  25. };

二叉树中序遍历 ,非递归迭代实现

  1. //从栈里面取到一个节点意味着这个节点的左子树已经访问完了
  2. //1、左路节点入栈
  3. //2、访问左路节点及左路节点的右子树
  4. class Solution {
  5. public:
  6. vector<int> inorderTraversal(TreeNode* root) {
  7. stack<TreeNode*> st;
  8. TreeNode* cur = root;
  9. vector<int> v;
  10. //访问一棵树的开始
  11. //1.访问左路节点,左路节点入栈,后续依次访问左路节点的右子树
  12. while (cur || !st.empty())
  13. {
  14. while (cur)
  15. {
  16. st.push(cur);
  17. cur = cur->left;
  18. }
  19. //依次访问左路节点及其右子树
  20. TreeNode* top = st.top();
  21. st.pop();
  22. v.push_back(top->val);
  23. //子问题的方式访问右子树
  24. cur = top->right;
  25. }
  26. return v;
  27. }
  28. };

二叉树的后序遍历 ,非递归迭代实现

  1. class Solution {
  2. public:
  3. vector<int> postorderTraversal(TreeNode* root) {
  4. stack<TreeNode*> st;
  5. TreeNode* prev = nullptr;
  6. TreeNode* cur = root;
  7. vector<int> v;
  8. while (cur || !st.empty())
  9. {
  10. while (cur)
  11. {
  12. st.push(cur);
  13. cur = cur->left;
  14. }
  15. TreeNode* top = st.top();
  16. //一个节点右子树为空或上一个访问的节点是右子树的根
  17. //那么都说明相当于右树访问过了可以访问当前根节点
  18. if (top->right == nullptr || top->right == prev)
  19. {
  20. prev = top;
  21. v.push_back(top->val);
  22. st.pop();
  23. }
  24. else
  25. {
  26. cur = top->right;
  27. }
  28. }
  29. return v;
  30. }
  31. };

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

闽ICP备14008679号