• 从零开始的C++(十六)


    二叉搜索树

    特点:

    1.左子树的值小于根的值

    2.右子树的值大于根的值

    3.左右子树均是二叉搜索树。

    用处:

    中序遍历可以得到有序排列。

    二叉搜索树的模拟实现:

    主要包括插入、删除、查找、遍历。

    1.二叉树的结点

    1. template<class K, class V>
    2. class BSTreeNode
    3. {
    4. public:
    5. BSTreeNode()
    6. {
    7. }
    8. BSTreeNode(const K& key, const V& value)
    9. :_key(key)
    10. , _value(value)
    11. {
    12. }
    13. K _key;
    14. V _value;
    15. BSTreeNode* _left;
    16. BSTreeNode* _right;
    17. };

    结点包括两个结点指针,一个K类型的变量,一个V类型的变量。此处K类型一般用于结点之间的大小比较,V类型用于存放某些相关信息。一般可以通过K查找V。

    2.搜索二叉树的实现

    1.插入:

    1. bool Insert(const K& key, const V& value)
    2. {
    3. if (_root == nullptr)
    4. {
    5. _root = new Node(key, value);
    6. return true;
    7. }
    8. else
    9. {
    10. Node* parent = nullptr;
    11. Node* cur = _root;
    12. while (cur != nullptr)
    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->_right;
    23. }
    24. else
    25. {
    26. return false;
    27. }
    28. }
    29. //判断左右
    30. if (parent->_key < key)
    31. {
    32. parent->_right = new Node(key, value);
    33. }
    34. else
    35. {
    36. parent->_left = new Node(key, value);
    37. }
    38. return true;
    39. }
    40. }

    对于插入,需要判断当前是否是空树,若是空树则只需要给_root开辟一个空间,并赋值即可。

    若是非空树,则需要确定插入的位置。由于搜索二叉树具有左结点值小于根结点,右节点值大于根节点的特性,因此可以快速确定插入位置。而parent指向插入位置的父节点,通过判断插入parent指向结点的左右即可完成插入。

    2.删除

    1. bool _Erase(Node*root,const K& key)
    2. {
    3. //先找结点
    4. Node* cur = root;
    5. Node* parent = nullptr;
    6. while (cur != nullptr)
    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
    19. {
    20. //找到的情况
    21. //1.左子树为空
    22. if (cur->_left == nullptr)
    23. {
    24. if (parent == nullptr)
    25. {
    26. parent = cur;
    27. _root = _root->_right;
    28. delete parent;
    29. parent = nullptr;
    30. return true;
    31. }
    32. else
    33. {
    34. if (parent->_key < cur->_key)
    35. {
    36. parent->_right = cur->_right;
    37. }
    38. else
    39. {
    40. parent->_left = cur->_right;
    41. }
    42. delete cur;
    43. cur = nullptr;
    44. return true;
    45. }
    46. }
    47. //2.右子树为空
    48. if (cur->_right == nullptr)
    49. {
    50. if (parent == nullptr)
    51. {
    52. parent = _root;
    53. _root = _root->_left;
    54. delete parent;
    55. parent = nullptr;
    56. return true;
    57. }
    58. else
    59. {
    60. if (parent->_key < cur->_key)
    61. {
    62. parent->_right = cur->_left;
    63. }
    64. else
    65. {
    66. parent->_left = cur->_left;
    67. }
    68. delete cur;
    69. cur = nullptr;
    70. return true;
    71. }
    72. }
    73. //3.左右子树均不为空
    74. //找cur右侧最小结点,保存在min中
    75. Node* min = cur->_right;
    76. Node* minparent = cur;
    77. while (min->_left != nullptr)
    78. {
    79. minparent = min;
    80. min = min->_left;
    81. }
    82. swap(cur->_key, min->_key);
    83. swap(cur->_value, min->_value);
    84. if (minparent == cur)
    85. {
    86. minparent->_right = min->_right;
    87. }
    88. else
    89. {
    90. minparent->_left = min->_right;
    91. }
    92. delete min;
    93. return true;
    94. }
    95. }
    96. return false;
    97. }
    98. bool Erase(const K& key)
    99. {
    100. return _Erase(_root, key);
    101. }

    删除首先需要找到要删除的结点,对于要删除的结点,分三种情况讨论:1.左子树为空。2.右子树为空。3左右子树均不为空。对于前两种情况,需要注意要删除结点是否是根结点,若是根结点,则直接修改根节点的值即可。若不是,则只需要单纯改变要删除结点的父节点的指向即可。对于第三种情况,需要找其右子树的最小结点与其交换(或者也可以找左子树的最大结点与之交换),交换的仅有K和V,对于指针指向不能修改。此处交换完值后,对于要删除的结点那个位置仍符合左子树值小于其,右子树值大于其。然后删除原本右子树的最小结点即可。

    3.查找:

    1. Node* Find(const K& key)
    2. {
    3. Node* cur = _root;
    4. while (cur!=nullptr)
    5. {
    6. if (cur->_key == key)
    7. {
    8. return cur;
    9. }
    10. else if (cur->_key < key)
    11. {
    12. cur = cur->_right;
    13. }
    14. else
    15. {
    16. cur = cur->_left;
    17. }
    18. }
    19. return nullptr;
    20. }

    利用搜索二叉树的特性可以快速完成查找。

    4.遍历:

    1. void _InOrder(Node* root)
    2. {
    3. if (root == nullptr)
    4. {
    5. return;
    6. }
    7. else
    8. {
    9. _InOrder(root->_left);
    10. cout << root->_key << ":" << root->_value << endl;
    11. _InOrder(root->_right);
    12. }
    13. }
    14. void InOrder()
    15. {
    16. //中序遍历
    17. _InOrder(_root);
    18. cout << endl;
    19. }

  • 相关阅读:
    IP-Guard管控应用程序运行有哪几种方式?
    2019史上最全java面试题题库大全800题含答案
    2.线性代数基础
    MySQL json数据类型应用
    css点击文字(非按钮) 能自动改变颜色。
    读取图片文件MetaFile放入Windows剪切板
    两日总结十三
    Flink K8s Operator 测试验证
    工业无线网关在实际生产中的应用效果和价值-天拓四方
    Ambire 的柏林冒险之旅,快来一起看看团队带来的日志的剪报吧!
  • 原文地址:https://blog.csdn.net/yyssas/article/details/134424213