• 二叉搜索树的实现(迭代方式)


    二叉搜索树(Binary Search Tree,BST)是一种基于二叉树的数据结构,具有以下特点:

    1. 对于树中的每个节点,其左子树中的所有节点的值都小于该节点的值,而右子树中的所有节点的值都大于该节点的值。
    2. 左子树和右子树也都是二叉搜索树。

    这个特性使得二叉搜索树能够高效地支持插入、删除和查找操作。

    插入操作:
    要在二叉搜索树中插入一个新节点,需要按照以下步骤进行:

    1. 如果树为空,则将新节点作为根节点。
    2. 如果树不为空,则从根节点开始比较新节点的值与当前节点的值。
    3. 如果新节点的值小于当前节点的值,继续在当前节点的左子树中插入新节点。
    4. 如果新节点的值大于当前节点的值,继续在当前节点的右子树中插入新节点。
    5. 重复步骤3和4,直到找到一个空位置插入新节点。

    删除操作:
    要在二叉搜索树中删除一个节点,需要按照以下步骤进行:

    1. 首先,找到要删除的节点。如果节点不存在,则删除操作失败。
    2. 如果要删除的节点没有子节点,直接删除即可。
    3. 如果要删除的节点只有一个子节点,将其子节点替代要删除的节点。
    4. 如果要删除的节点有两个子节点,需要找到其右子树中的最小节点(或左子树中的最大节点),将其值替换到要删除的节点上,然后删除这个最小(或最大)节点。

    针对第4点,左子树的最右节点和右子树的最左节点,只有这两个节点能起到承上启下的作用,比所有的左子树节点都大,比所有的右子树节点都小;去替换目标节点后,这颗二叉树才能保持为搜索二叉树。

    查找操作:
    要在二叉搜索树中查找一个特定的值,需要按照以下步骤进行:

    1. 从根节点开始,将要查找的值与当前节点的值进行比较。
    2. 如果要查找的值等于当前节点的值,则找到了目标节点,返回该节点。
    3. 如果要查找的值小于当前节点的值,继续在当前节点的左子树中查找。
    4. 如果要查找的值大于当前节点的值,继续在当前节点的右子树中查找。
    5. 重复步骤3和4,直到找到目标节点或者遍历到叶子节点为止。

    二叉搜索树的时间复杂度取决于树的平衡性。在最坏情况下,树可能变成链表,导致插入、删除和查找操作的时间复杂度为 O(n)。但是,如果树保持平衡,例如平衡二叉搜索树(AVL树、红黑树等),则这些操作的时间复杂度可以保持在 O(log n)。

    以下是BST的代码实现:

    定义BST的节点结构BSTreeNode,包含左子树指针_left、右子树指针_right和键值_key

    1. #include
    2. using namespace std;
    3. template<class K>
    4. struct BSTreeNode
    5. {
    6. BSTreeNode* _left;
    7. BSTreeNode* _right;
    8. K _key;
    9. BSTreeNode(const K& key)
    10. :_left(nullptr)
    11. , _right(nullptr)
    12. , _key(key)
    13. { }
    14. };

    定义BSTree类,其中的Find函数用于查找指定的键值。从根节点开始,根据当前节点的键值与目标键值的大小关系,沿着左子树或右子树逐步查找,直到找到目标键值或遍历完整个树。

    1. template<class K>
    2. class BSTree
    3. {
    4. typedef BSTreeNode Node;
    5. public:
    6. bool Find(const K& key)
    7. {
    8. Node* cur = _root;
    9. while (cur)
    10. {
    11. if (cur->_key < key)
    12. {
    13. cur = cur->_right;
    14. }
    15. else if (cur->_key > key)
    16. {
    17. cur = cur->_left;
    18. }
    19. else
    20. return true;
    21. }
    22. return false;
    23. }
    1. bool Erase(const K& key)
    2. {
    3. if (_root == nullptr)
    4. return false;
    5. Node* cur = _root;
    6. Node* parent = nullptr;
    7. while (cur)
    8. {
    9. //不能在这更新parent,否则删除节点时parent和cur指向相同
    10. if (key < cur->_key)
    11. {
    12. parent = cur;
    13. cur = cur->_left;
    14. }
    15. else if (key > cur->_key)
    16. {
    17. parent = cur;
    18. cur = cur->_right;
    19. }
    20. else//删除操作
    21. {
    22. //只有一个孩子,托孤法
    23. if (cur->_right == nullptr)//只有一个左孩子(+无孩子)
    24. {
    25. if (parent == nullptr)
    26. {
    27. _root = cur->_left;
    28. delete cur;
    29. return true;
    30. }
    31. if (cur == parent->_left)
    32. parent->_left = cur->_left;
    33. else
    34. parent->_right = cur->_left;
    35. delete cur;
    36. }
    37. else if (cur->_left == nullptr)//只有一个右孩子
    38. {
    39. if (parent == nullptr)
    40. {
    41. _root = cur->_right;
    42. delete cur;
    43. return true;
    44. }
    45. if (cur == parent->_left)
    46. parent->_left = cur->_right;
    47. else
    48. parent->_right = cur->_right;
    49. delete cur;
    50. }
    51. else //左右都有孩子
    52. {
    53. //有两个孩子,替换删除法;左子树的最大(最右)节点或右子数的最小(最左)节点
    54. //找到右子树的最左节点
    55. Node* subLeft = cur->_right;
    56. Node* subParent = cur;
    57. while (subLeft->_left)
    58. {
    59. subParent = subLeft;
    60. subLeft = subLeft->_left;
    61. }
    62. swap(subLeft->_key, cur->_key);
    63. //经过交换后,现在只需删除subLeft,已经转化为只有一个孩子的情况,使用托孤法
    64. if (subParent->_left == subLeft)
    65. subParent->_left = subLeft->_right;
    66. else
    67. subParent->_right = subLeft->_right;
    68. delete subLeft;
    69. }
    70. return true;
    71. }
    72. }
    73. return false;
    74. }

    Erase函数用于删除指定的键值对应的节点。删除操作分为四种情况:

    1. 节点只有一个左孩子(包括无孩子),使用"托孤法"将其父节点指向其左孩子。
    2. 节点只有一个右孩子,同样使用"托孤法"将其父节点指向其右孩子。
    3. 节点既有左孩子又有右孩子,采用"替换删除法",找到右子树的最左节点(或左子树的最右节点),将其键值与当前节点键值交换,然后删除这个最左节点。
    4. 无孩子可以随便合并到只有一个孩子的情况,代码是兼容的。

    1. bool insert(const K& key)
    2. {
    3. if (_root == nullptr)
    4. {
    5. _root = new Node(key);
    6. return true;
    7. }
    8. else
    9. {
    10. Node* parent = nullptr;
    11. Node* cur = _root;
    12. while (cur)
    13. {
    14. //parent = cur; 虽然写这里也可以,但是写在里面结构更清晰
    15. if (cur->_key > key)
    16. {
    17. parent = cur;
    18. cur = cur->_left;
    19. }
    20. else if (cur->_key < key)
    21. {
    22. parent = cur;
    23. cur = cur->_right;
    24. }
    25. else
    26. return false;
    27. }
    28. cur = new Node(key);
    29. parent->_key > cur->_key ? parent->_left = cur : parent->_right = cur;
    30. }
    31. }

    insert函数用于插入一个新节点。若树为空,直接创建根节点;否则,从根节点开始,根据当前节点的键值与待插入键值的大小关系,沿着左子树或右子树逐步查找插入位置,直到找到一个空位置,然后创建新节点并插入。

    1. void midOrder()
    2. {
    3. _midOrder(_root);
    4. }
    5. private:
    6. Node * _root = nullptr;
    7. void _midOrder(Node* node)
    8. {
    9. if (node == nullptr)
    10. return;
    11. _midOrder(node->_left);
    12. cout << node->_key << " ";
    13. _midOrder(node->_right);
    14. }
    15. };

    midOrder函数用于中序遍历树。由于根节点指针_root是私有的,无法在类外直接使用,因此提供了一个公有的无参遍历函数,在类内部调用私有的递归遍历函数_midOrder

    1. int main()
    2. {
    3. int a[] = { 8,3,1,6,4,7,14,13 };
    4. BSTree<int> bst;
    5. for (int x : a)
    6. {
    7. bst.insert(x);
    8. }
    9. //测试:遍历删除
    10. for (int x : a)
    11. {
    12. bst.midOrder();
    13. cout << endl;
    14. bst.Erase(x);
    15. bst.midOrder();
    16. cout << endl;
    17. cout << endl;
    18. }
    19. cout << "全部删除成功" << endl;
    20. system("pause");
    21. return 0;
    22. }

    在主函数中,首先创建了一个BSTree对象bst,然后依次插入数组a中的元素。接下来进行遍历删除的测试,每次删除一个元素后,都会输出当前树的中序遍历结果。最后输出"全部删除成功",并暂停程序的执行。

    这段代码实现了BST的基本功能,包括查找、插入和删除操作,并且通过遍历删除的测试验证了代码的正确性。

    这是插入后建立的二叉树的逻辑关系:

    完整代码如下:

    1. #include
    2. using namespace std;
    3. template<class K>
    4. struct BSTreeNode
    5. {
    6. BSTreeNode* _left;
    7. BSTreeNode* _right;
    8. K _key;
    9. BSTreeNode(const K& key)
    10. :_left(nullptr)
    11. , _right(nullptr)
    12. , _key(key)
    13. { }
    14. };
    15. template<class K>
    16. class BSTree
    17. {
    18. typedef BSTreeNode Node;
    19. public:
    20. bool Find(const K& key)
    21. {
    22. Node* cur = _root;
    23. while (cur)
    24. {
    25. if (cur->_key < key)
    26. {
    27. cur = cur->_right;
    28. }
    29. else if (cur->_key > key)
    30. {
    31. cur = cur->_left;
    32. }
    33. else
    34. return true;
    35. }
    36. return false;
    37. }
    38. //删除节点,四种情况,可以合并为三种执行
    39. bool Erase(const K& key)
    40. {
    41. if (_root == nullptr)
    42. return false;
    43. Node* cur = _root;
    44. Node* parent = nullptr;
    45. while (cur)
    46. {
    47. //不能在这更新parent,否则删除节点时parent和cur指向相同
    48. if (key < cur->_key)
    49. {
    50. parent = cur;
    51. cur = cur->_left;
    52. }
    53. else if (key > cur->_key)
    54. {
    55. parent = cur;
    56. cur = cur->_right;
    57. }
    58. else//删除操作
    59. {
    60. //只有一个孩子,托孤法
    61. if (cur->_right == nullptr)//只有一个左孩子(+无孩子)
    62. {
    63. if (parent == nullptr)
    64. {
    65. _root = cur->_left;
    66. delete cur;
    67. return true;
    68. }
    69. if (cur == parent->_left)
    70. parent->_left = cur->_left;
    71. else
    72. parent->_right = cur->_left;
    73. delete cur;
    74. }
    75. else if (cur->_left == nullptr)//只有一个右孩子
    76. {
    77. if (parent == nullptr)
    78. {
    79. _root = cur->_right;
    80. delete cur;
    81. return true;
    82. }
    83. if (cur == parent->_left)
    84. parent->_left = cur->_right;
    85. else
    86. parent->_right = cur->_right;
    87. delete cur;
    88. }
    89. else //左右都有孩子
    90. {
    91. //有两个孩子,替换删除法;左子树的最大(最右)节点或右子数的最小(最左)节点
    92. //找到右子树的最左节点
    93. Node* subLeft = cur->_right;
    94. Node* subParent = cur;
    95. while (subLeft->_left)
    96. {
    97. subParent = subLeft;
    98. subLeft = subLeft->_left;
    99. }
    100. swap(subLeft->_key, cur->_key);
    101. //经过交换后,现在只需删除subLeft,已经转化为只有一个孩子的情况,使用托孤法
    102. if (subParent->_left == subLeft)
    103. subParent->_left = subLeft->_right;
    104. else
    105. subParent->_right = subLeft->_right;
    106. delete subLeft;
    107. }
    108. return true;
    109. }
    110. }
    111. return false;
    112. }
    113. //插入节点,要保证插入的值不同,一旦发现相同的,返回false
    114. bool insert(const K& key)
    115. {
    116. if (_root == nullptr)
    117. {
    118. _root = new Node(key);
    119. return true;
    120. }
    121. else
    122. {
    123. Node* parent = nullptr;
    124. Node* cur = _root;
    125. while (cur)
    126. {
    127. //parent = cur; 虽然写这里也可以,但是写在里面结构更清晰
    128. if (cur->_key > key)
    129. {
    130. parent = cur;
    131. cur = cur->_left;
    132. }
    133. else if (cur->_key < key)
    134. {
    135. parent = cur;
    136. cur = cur->_right;
    137. }
    138. else
    139. return false;
    140. }
    141. cur = new Node(key);
    142. parent->_key > cur->_key ? parent->_left = cur : parent->_right = cur;
    143. }
    144. }
    145. //遍历树需要传根节点指针,但是树的指针是私有的
    146. //解决方法:套一层壳,给用户提供无参的遍历函数,在类内部调用传参遍历函数
    147. void midOrder()
    148. {
    149. _midOrder(_root);
    150. }
    151. private:
    152. Node * _root = nullptr;
    153. void _midOrder(Node* node)
    154. {
    155. if (node == nullptr)
    156. return;
    157. _midOrder(node->_left);
    158. cout << node->_key << " ";
    159. _midOrder(node->_right);
    160. }
    161. };
    162. int main()
    163. {
    164. int a[] = { 8,3,1,6,4,7,14,13 };
    165. BSTree<int> bst;
    166. for (int x : a)
    167. {
    168. bst.insert(x);
    169. }
    170. //测试:遍历删除
    171. for (int x : a)
    172. {
    173. bst.midOrder();
    174. cout << endl;
    175. bst.Erase(x);
    176. bst.midOrder();
    177. cout << endl;
    178. cout << endl;
    179. }
    180. cout << "全部删除成功" << endl;
    181. system("pause");
    182. return 0;
    183. }

    以上是BST的实现是以迭代方式(循环),下一篇博客会展示以递归方式实现BST的增、删、查。

  • 相关阅读:
    Keytool配置 Tomcat的HTTPS双向认证
    CSS小计
    健壮性测试是什么?
    mysql事务和事务隔离级别
    Muduo网络库之Channel、EPollPoller与EventLoop类【深度解析】
    国内一些镜像源
    RCU Library
    【AutoSAR CAN】02 - 硬件过滤器配置
    3D医学影像PACS系统源代码
    加密与解密笔记-4.1
  • 原文地址:https://blog.csdn.net/weixin_73567058/article/details/134052295