• 数据结构 六 理解二叉搜索树的实现


    数据结构就是定义出某种结构:像数组结构、链表结构、树形结构等,实现数据结构就是我们主动去管理增删查改的实现函数

    二叉搜索树的概念

    二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树

    非空左子树上所有节点的值都小于根节点的值,右子树上所有节点的值都大于根节点的值

    二叉搜索树的定义

    1. template<class K>//搜索二叉树
    2. struct BSTreeNode
    3. {
    4. BSTreeNode* _left;//左孩子指针
    5. BSTreeNode* _right;//右孩子指针
    6. K _key;//节点数据值
    7. //构造函数
    8. BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key)
    9. {}
    10. };

    先来了解一下文件BinnarySearchTree.h中的接口

    1. template<class K>
    2. class BSTree
    3. {
    4. typedef BSTreeNode Node; //重命名上面的定义
    5. public
    6. //递归查找
    7. bool FindR(const K& key)
    8. {
    9. return _FindR(_root, key);
    10. }
    11. //递归插入
    12. bool InsertR(const K& key)
    13. {
    14. return _InsertR(_root, key);
    15. }
    16. //递归删除
    17. bool EraseR(const K& key)
    18. {
    19. return _EraseR(_root, key);
    20. }
    21. //中序遍历 类内调用拿root
    22. void InOrder()
    23. {
    24. _InOrder(_root);
    25. cout << endl;
    26. }
    27. //构造函数
    28. BSTree() = default;
    29. //拷贝构造
    30. BSTree(const BSTree& t);
    31. //赋值函数
    32. BSTree& operator=(BSTree t);
    33. //析构函数
    34. ~BSTree();
    35. private:
    36. //类部子函数不继承的话通常私有
    37. bool _EraseR(Node*& root,const K& key);//递归删除
    38. bool _InsertR(Node*& root,const K& key);//递归插入
    39. bool _FindR(Node* root,const K& key);//递归查找
    40. void _InOrder(Node* root);//中序遍历函数
    41. void DestroyTree(Node* root)//置空函数
    42. Node* CopyTree(Node* root)//树的拷贝
    43. private:
    44. Node* _root = nullptr;
    45. };

    下面我们来详细学习函数接口的具体实现

    递归插入函数的定义

    1. //递归插入函数
    2. bool _InsertR(Node*& root,const K& key)//递归插入
    3. {
    4. if (root == nullptr)//为空就申请节点插入数据
    5. {
    6. root = new Node(key);//注意上面传指针引用 可以直接连接
    7. return true;
    8. }
    9. if (key > root->_key)//插入的值大于根 往右走
    10. {
    11. return _InsertR(root->_right, key);
    12. }
    13. else if (key < root->_key)//插入的值小于根 往右走
    14. {
    15. return _InsertR(root->_left, key);
    16. }
    17. else
    18. {
    19. return false;//相等说明数据已经有了
    20. }
    21. }

    递归删除函数的定义

    1. bool _EraseR(Node*& root,const K& key)//递归删除
    2. {
    3. //先找到要删除的节点
    4. if (root == nullptr)//为空返回false
    5. {
    6. return false;
    7. }
    8. if (key > root->_key)//要删除的大于根
    9. {
    10. return _EraseR(root->_right, key);//递归右树找
    11. }
    12. else if (key < root->_key)//要删除的小于根
    13. {
    14. return _EraseR(root->_left, key);//递归左树找
    15. }
    16. //走到这里说明已经找到要删除的数据
    17. else
    18. {
    19. //1.删除一个孩子 左为空或右为空 托孤
    20. Node* del = root;//先保存要删除的节点
    21. if (root->_left == nullptr)//左为空 让父亲指向右
    22. {
    23. root = root->_right;//引用
    24. }
    25. else if (root->_right == nullptr)右为空 让父亲指向左
    26. {
    27. root = root->_left;
    28. }
    29. //2.删除2个孩子左右都不为空 替换法
    30. else
    31. {
    32. Node* minRight = root->_right;//
    33. while (minRight->_left)
    34. {
    35. minRight = minRight->_left;
    36. }
    37. swap(root->_key, minRight->_key);//交换数据
    38. //此时要删除的已经换下来 再去右树递归删除
    39. return _EraseR(root->_right, key);//指定树
    40. }
    41. delete del;
    42. return true;
    43. }
    44. }

    中序遍历函数的定义

    1. //中序遍历
    2. void _InOrder(Node* root) 左子树 根 右子树
    3. {
    4. if (root == nullptr)
    5. return;
    6. _InOrder(root->_left);//先走左边
    7. cout << root->_key << " "; 打印数据
    8. _InOrder(root->_right);//后走右边
    9. }

      我们先用以上接口实现图中二叉树的插入和删除测试案例TestBSTree

    1. //递归插入测试
    2. void TestBSTree1()
    3. {
    4. //
    5. BSTree<int> t;
    6. int a[] = { 8,3,1,10,6,4,7,14,13, };
    7. for (auto e : a)
    8. {
    9. t.InsertR(e);//依次插入 默认去重
    10. }
    11. t.InOrder();//遍历打印 1 3 4 6 7 8 10 13 14
    12. }
    13. //递归删除测试
    14. void TestBSTree2()
    15. {
    16. //
    17. BSTree<int> t;
    18. int a[] = { 8,3,1,10,6,4,7,14,13, };
    19. for (auto e : a)
    20. {
    21. t.InsertR(e);//依次插入 默认去重
    22. }
    23. t.InOrder();//打印1 3 4 6 7 8 10 13 14
    24. t.EraseR(14);//删除14
    25. t.InOrder();//打印1 3 4 6 7 8 10 13
    26. }
    27. int main()
    28. {
    29. TestBSTree1();//递归插入
    30. TestBSTree2();//递归删除
    31. return 0;
    32. }

    递归查找函数的定义

    从根开始比较查找,比根大则往右边查找,比根小则往左边查找

    1. bool _FindR(Node* root,const K& key)//递归查找
    2. {
    3. if (root == nullptr)
    4. return false;
    5. if (key > root->_key)//
    6. {
    7. return _FindR(root->_right, key);
    8. }
    9. else if(key < root->_key)
    10. {
    11. return _FindR(root->_left, key);
    12. }
    13. else
    14. {
    15. return true;//相等就找到了
    16. }
    17. }

    置空函数定义

    置空函数一般会放在我们进行插入或删除的函数最后,释放我们在堆上申请的空间,将其还给操作系统,另外也会相应的进行检查越界等问题

    1. void DestroyTree(Node* root)//置空函数
    2. {
    3. if (root == nullptr)
    4. return;
    5. DestroyTree(root->_left);
    6. DestroyTree(root->_right);
    7. delete root;
    8. }

    析构函数定义

    1. ~BSTree()//析构函数
    2. {
    3. DestroyTree(_root);//调用置空函数
    4. _root = nullptr;
    5. }

    赋值函数定义

    1. BSTree& operator=(BSTree t)//赋值函数
    2. {
    3. swap(_root, t._root);
    4. return *this;
    5. }

    树的拷贝函数定义

    1. Node* CopyTree(Node* root)//拷贝树
    2. {
    3. if (root == nullptr)
    4. return nullptr;
    5. Node* copynode = new Node(root->_key);
    6. copynode->_left = CopyTree(root->_left);//递归创建左树
    7. copynode->_right = CopyTree(root->_right);//递归创建右树
    8. return copynode;
    9. }

    拷贝构造函数定义

    1. BSTree(const BSTree& t)//拷贝构造
    2. {
    3. _root = CopyTree(t._root);//调用下方树的拷贝函数
    4. cout << "调用拷贝构造" << endl;
    5. }

    我们再用上面这几个接口实现第3个测试案例TestBSTree3

    1. //赋值函数测试
    2. void TestBSTree3()
    3. {
    4. //
    5. BSTree<int> t;
    6. int a[] = { 8,3,1,10,6,4,7,14,13, };
    7. for (auto e : a)
    8. {
    9. t.InsertR(e);//依次插入 默认去重
    10. }
    11. t.InOrder();//遍历打印 1 3 4 6 7 8 10 13 14
    12. BSTree<int> t1;
    13. t1 = t;//赋值函数 调用拷贝构造
    14. t.InOrder();//遍历打印1 3 4 6 7 8 10 13 14
    15. }
    16. int main()
    17. {
    18. TestBSTree3();//赋值函数测试
    19. return 0;
    20. }

    最后这里放一下整个二叉搜索树递归版本代码的实现,方便大家观察理解

    1. #pragma once
    2. template<class K>//搜索二叉树
    3. struct BSTreeNode
    4. {
    5. BSTreeNode* _left;
    6. BSTreeNode* _right;
    7. K _key;
    8. //构造函数
    9. BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key)
    10. {}
    11. };
    12. template<class K>
    13. class BSTree
    14. {
    15. typedef BSTreeNode Node;
    16. public:
    17. //默认构造
    18. BSTree() = default;
    19. //拷贝构造
    20. BSTree(const BSTree& t)
    21. {
    22. _root = CopyTree(t._root);
    23. cout << "调用拷贝构造" << endl;
    24. }
    25. //赋值函数
    26. BSTree& operator=(BSTree t)
    27. {
    28. swap(_root, t._root);
    29. return *this;
    30. }
    31. //析构函数
    32. ~BSTree()
    33. {
    34. DestroyTree(_root);
    35. _root = nullptr;
    36. }
    37. //递归查找
    38. bool FindR(const K& key)
    39. {
    40. return _FindR(_root, key);
    41. }
    42. //递归插入
    43. bool InsertR(const K& key)
    44. {
    45. return _InsertR(_root, key);
    46. }
    47. //递归删除
    48. bool EraseR(const K& key)
    49. {
    50. return _EraseR(_root, key);
    51. }
    52. //中序遍历 类内调用拿root
    53. void InOrder()
    54. {
    55. _InOrder(_root);
    56. cout << endl;
    57. }
    58. private://类部子函数不继承的话通常私有
    59. //递归删除
    60. bool _EraseR(Node*& root,const K& key)
    61. {
    62. if (root == nullptr)
    63. {
    64. return false;
    65. }
    66. if (key > root->_key)//
    67. {
    68. return _EraseR(root->_right, key);
    69. }
    70. else if (key < root->_key)
    71. {
    72. return _EraseR(root->_left, key);
    73. }
    74. else
    75. {
    76. Node* del = root;//保存要删除的节点
    77. //相等说明可以删除了 三种情况
    78. if (root->_left == nullptr)
    79. {
    80. root = root->_right;//引用
    81. }
    82. else if (root->_right == nullptr)
    83. {
    84. root = root->_left;
    85. }
    86. else//左右都不为空 替换法
    87. {
    88. Node* minRight = root->_right;
    89. while (minRight->_left)
    90. {
    91. minRight = minRight->_left;
    92. }
    93. swap(root->_key, minRight->_key);//交换数据
    94. //此时要删除的已经换下来 再去右树递归删除
    95. return _EraseR(root->_right, key);//指定树
    96. }
    97. delete del;
    98. return true;
    99. }
    100. }
    101. bool _InsertR(Node*& root,const K& key)//递归插入
    102. {
    103. if (root == nullptr)//为空就申请节点插入数据
    104. {
    105. root = new Node(key);//注意上面传指针引用 可以直接连接
    106. return true;
    107. }
    108. if (key > root->_key)//插入的值大于根 往右走
    109. {
    110. return _InsertR(root->_right, key);
    111. }
    112. else if (key < root->_key)//插入的值小于根 往右走
    113. {
    114. return _InsertR(root->_left, key);
    115. }
    116. else
    117. {
    118. return false;//相等说明数据已经有了
    119. }
    120. }
    121. bool _FindR(Node* root,const K& key)//递归查找
    122. {
    123. //
    124. if (root == nullptr)
    125. return false;
    126. if (key > root->_key)//
    127. {
    128. return _FindR(root->_right, key);
    129. }
    130. else if(key < root->_key)
    131. {
    132. return _FindR(root->_left, key);
    133. }
    134. else
    135. {
    136. return true;//相等就找到了
    137. }
    138. }
    139. void _InOrder(Node* root)//中序遍历
    140. {
    141. if (root == nullptr)
    142. return;
    143. _InOrder(root->_left);
    144. cout << root->_key << " ";
    145. _InOrder(root->_right);
    146. }
    147. void DestroyTree(Node* root)//置空函数
    148. {
    149. if (root == nullptr)
    150. return;
    151. DestroyTree(root->_left);
    152. DestroyTree(root->_right);
    153. delete root;
    154. }
    155. Node* CopyTree(Node* root)//拷贝树
    156. {
    157. if (root == nullptr)
    158. return nullptr;
    159. Node* copynode = new Node(root->_key);
    160. copynode->_left = CopyTree(root->_left);//递归创建左树
    161. copynode->_right = CopyTree(root->_right);//递归创建右树
    162. return copynode;
    163. }
    164. private:
    165. Node* _root = nullptr;
    166. };
    167. //递归插入测试
    168. void TestBSTree1()
    169. {
    170. BSTree<int> t;
    171. int a[] = { 8,3,1,10,6,4,7,14,13, };
    172. for (auto e : a)
    173. {
    174. t.InsertR(e);//依次插入 默认去重
    175. }
    176. t.InOrder();//遍历
    177. }
    178. //递归删除测试
    179. void TestBSTree2()
    180. {
    181. BSTree<int> t;
    182. int a[] = { 8,3,1,10,6,4,7,14,13, };
    183. for (auto e : a)
    184. {
    185. t.InsertR(e);//依次插入 默认去重
    186. }
    187. t.InOrder();//打印 1 3 4 6 7 8 10 13 14
    188. t.EraseR(8);//删除8
    189. t.EraseR(3);//删除3
    190. t.InOrder();//打印 1 4 6 7 10 13 14
    191. }
    192. //赋值函数测试
    193. void TestBSTree3()
    194. {
    195. BSTree<int> t;
    196. int a[] = { 8,3,1,10,6,4,7,14,13, };
    197. for (auto e : a)
    198. {
    199. t.InsertR(e);//依次插入 默认去重
    200. }
    201. t.InOrder();//遍历打印 1 3 4 6 7 8 10 13 14
    202. BSTree<int> t1;
    203. t1 = t;//赋值函数 调用拷贝构造
    204. t.InOrder();//遍历打印1 3 4 6 7 8 10 13 14
    205. }
    206. int main()
    207. {
    208. TestBSTree1();//插入函数测试
    209. TestBSTree2();//删除函数测试
    210. TestBSTree3();//赋值函数测试
    211. return 0;
    212. }

    在Java和C++的学习当中,前期学习数据结构当中的顺序表、链表、二叉树等便于我们后面更好的学习容器,后面会继续分享红黑树和排序的实现

    希望这篇文章大家有所收获,我们下篇见

  • 相关阅读:
    机器学习知识总结 —— 15. 什么是支持向量机(对偶问题、核技巧)?
    Maximum And Queries (easy version)
    Java虚拟机(JVM)-- Dump内存快照
    代码分析之今日头条
    Windows10系统下安装GPU版Pytorch和MMDetection
    Docker 启动容器
    jQuery获取更改标签内容、操作标签属性:html()、text()、val()、attr()、prop()
    论文基本功——LaTeX:公式及其编号
    nginx常用命令
    Java特性之设计模式【备忘录模式】
  • 原文地址:https://blog.csdn.net/qq_72486849/article/details/126016282