• C++ BinarySercahTree for version


    目录

    搜索二叉树定义

     搜索二叉树模拟实现

     插入

    遍历

    查找

    删除

    托管代码


    搜索二叉树定义

     搜索二叉树模拟实现

    首先写一个模版,然后写一个搜索二叉树的类 BSTree,类里面给 BSTe进行重命名为:Node。

    1. template<class K>
    2. class BSTree
    3. {
    4. tyepdef BSTree Node;
    5. private:
    6. Node* root = nullptr;
    7. };

    再写一个搜二叉的结构体

    1. template<class K>
    2. struct BSTreeNode
    3. {
    4. BSTreeNode< K>* _left;
    5. BSTreeNode< K>* _right;
    6. K _key;
    7. };

     初始化列表

    1. struct BSTreeNode
    2. {
    3. BSTreeNode< K>* _left;
    4. BSTreeNode< K>* _right;
    5. K _key;
    6. BSTree
    7. :_left(nullptr)
    8. ,_right(nullptr)
    9. ,_key(key)
    10. {
    11. }
    12. };

     插入

    如果根节点为空,就开辟一个节点,然后把要插入的值给这个节点:

    1. bool Insert(const k& key)
    2. {
    3. if (_root == nullptr)
    4. {
    5. _root = new Node(key);
    6. return true;
    7. }
    8. }

     如果跟节点不为空,就按搜索二叉树的性质来插入


    即:如果key值比root值大,就把key值插在root节点右边,否则就插在root节点左边。

    假如下图这个搜二叉,我们要插入个key=16,该怎么插:

     插入的历程如下:

     code

    1. else{
    2. Node* cur = _root;
    3. if (cur->_key < key)//如果key大
    4. {
    5. cur = cur->_right; //往右走
    6. }
    7. else if (cur->_key > key) //如果key小
    8. {
    9. cur = cur->_left; //往左走
    10. }
    11. }

    还有一种情况,那就是如果插入值和cur相等呢?

    搜二叉不允许节点值相等,所以这就是为什么Insert函数给bool类型的原因。

    如果相等就return false;

    1. else
    2. {
    3. Node* cur = _root;
    4. if (cur->_key < key)
    5. {
    6. cur = cur->_right;
    7. }
    8. else if (cur->_key > key)
    9. {
    10. cur = cur->_left;
    11. }
    12. else
    13. {
    14. return false;
    15. }

    上面的只是走一次,走到哪里结束呢?还需要再加个限制条件,我们让cur走到空的时候就不走了

     当while(cur为空就不走了):

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

    开始插入

    在为空的地方开辟一个新节点,把要插入的值push进这个新节点

    1. else
    2. {
    3. while (cur)
    4. {
    5. Node* cur = _root;
    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 false;
    17. }
    18. }
    19. cur = new Node(key);
    20. return true;
    21. }

    ps:

    虽然把值push进新节点了,但是此时新节点与搜二叉是一个断层状态,我们还需要把它们链起来。

    可以弄一个快慢指针,cur先走,parent再走,当cur走完了,parent正好是cur的父亲节点,再把他俩链起来。

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

    遍历

    写一个中序来遍历

    1. void Inorder(Node * root)
    2. {
    3. if (root == nullptr)return;
    4. Inorder(root->_left);
    5. cout << root->_key << " ";
    6. Inorder(root->_right);
    7. }cout << endl;

    写完了我们现在要调用Inorder

    1. main
    2. BSTree<int> bt;
    3. bt.Inorder();

    递归调用需要传参,我们应该传个root给inorder()。但是我们在main函数里拿不到root,列如:

     

    方法一,写一个getroot函数,返回root,我们调用getroot就行了:

    1. Node* getroot()
    2. {
    3. return root;
    4. }
    1. main
    2. bt.Inorder(bt.getroot());

    方法2:我们外层写一个递归INORDER(),里面嵌套我们的_INORDER(),以此实现递归:

    1. void _Inorder(Node * root)
    2. {
    3. if (root == nullptr)return;
    4. _Inorder(root->_left);
    5. cout << root->_key << " ";
    6. _Inorder(root->_right);
    7. }cout << endl;
    8. void Inorder()
    9. {
    10. _Inoreder(_root);
    11. }
    1. main
    2. bt.Inorder( );

    查找

    查找key值,如果key比cur大就往右找,否则往左找,如果找到节点为空都找不到,返回false。如果找到了,返回true

    1. bool Find(const K& key)
    2. {
    3. Node* cur = _root;
    4. if (_root == nullptr)return;
    5. if (cur->_key < key)
    6. {
    7. cur = cur->right;
    8. }
    9. else if (cur->_key > key)
    10. {
    11. cur = cur->left;
    12. }
    13. else
    14. {
    15. //找遍了就是没找到,return false;
    16. return false;
    17. }
    18. return true;//否则找到节点为空就是找到了,return true;
    19. }

    删除

    我们都知道叶子节点最好删除,只要置空,然后free掉就可以了,如下图:

    假设我们要删除根节点呢?

    根据之前写二叉树的经验,根节点不能直接删除,再把其他节点挪上来,否则会改变树的结构。

    而根节点处理好了,删除其他节点都可以按一样的方法进行处理。

    我们可以用替换法,就是找一个节点与要交换的节点进行交换,但是交换之后仍然要保证搜二叉地的特性。

    比如我可以把值为8节点和值为7节点进行交换:也可以把值为8节点与值为10节点交换:

    我们发现规律:与左子树最大的值的节点交换/与右子树最小的值的节点交换。

    然后再置空,free:

    注意点:先写出框架:

    1. bool Erase(const K& key )
    2. {
    3. Node* parent = nullptr; //初始化
    4. Node* cur = root;
    5. //要删除数如果大于root就往右走
    6. if (cur->_key < key)
    7. {
    8. cur = cur->right;
    9. }
    10. //要删除数如果大于root就往左走
    11. else if (cur->_key > key)
    12. {
    13. cur = cur->left;
    14. }
    15. else
    16. {
    17. //走到空,相等了,相等就是找到了
    18. //找到了就准备删除
    19. }
    20. }

    然后来写删除的具体步骤:

    我们把8删掉了,就断层了:

    我们需要重新链起来,可以这样写:

    key->right=root->right

    但是这样不完善,因为万一被删除的节点左边还有节点呢?

    所以还要写一个

    key->left=root->right

    code

    1. Node* parent = nullptr; //初始化
    2. Node* cur = root;
    3. if (cur->left == nullptr)
    4. {
    5. if (cur = parent->left)
    6. {
    7. parent->left = cur->right;
    8. }
    9. else if(cur = parent->right)
    10. {
    11. parent->right = cur->right;
    12. }
    13. }
    14. else if (cur->right == nullptr)
    15. {
    16. if (cur = parent->left)
    17. {
    18. parent->left = cur->left;
    19. }
    20. else if (cur = parent->right)
    21. {
    22. parent->right = cur->left;
    23. }
    24. }

    诈一看,好像没什么大问题,但实际上有纰漏。代码漏了一种情况,那就是下图这种情况:

     cur=8=root=key,8就是我们要删除的节点,8没有parent。

    所以还需要加个条件:

    1. if (cur == parent->left)
    2. {
    3. cur = cur->right;
    4. }

     

    1. if (cur = parent->left)
    2. {
    3. parent->left = cur->left;
    4. }

     还有一种情况,就是左右子树都不为空,例如3节点:

    现在我们要删除3节点怎么删:

    还是上面的方法,要么找右子树的最左节点(也就是右子树最小节点)或者找左子树最由节点(也就是左子树最大节点),然后进行替换。

    拿找右子树的最左节点举例:
     

    先向右走

    Node* subleft = cur->_right;

     

     然后再判断右子树的左分支子树是否为空,不为空就往左走,走到空为止:

    1. else
    2. {
    3. Node* parent = cur;
    4. Node* subleft = cur->_right;
    5. while (subleft->left)
    6. {
    7. parent = subleft;
    8. subleft = subleft->_left;
    9. }
    10. }

    找到节点之后进行交换:

     

    1. swap(cur->_key, subLeft->_key);
    2. if (subLeft == parent->_left)
    3. parent->_left = subLeft->_right;
    4. else
    5. parent->_right = subLeft->_right;
    6. }

    然后return true显示找到了值并且删除了:
     

    return true;

    整个删除就结束了,如果没有找到并且删除就return fales;

    return false;

    Erase完整code

    1. bool Erase(const K& key)
    2. {
    3. Node* parent = nullptr;
    4. Node* cur = _root;
    5. while (cur)
    6. {
    7. if (cur->_key < key)
    8. {
    9. parent = cur;
    10. cur = cur->_right;
    11. }
    12. else if (cur->_key > key)
    13. {
    14. parent = cur;
    15. cur = cur->_left;
    16. }
    17. else
    18. {
    19. // 准备删除 20:15继续
    20. if (cur->_left == nullptr)
    21. {//左为空
    22. if (cur == _root)
    23. {
    24. _root = cur->_right;
    25. }
    26. else
    27. {
    28. if (cur == parent->_left)
    29. {
    30. parent->_left = cur->_right;
    31. }
    32. else
    33. {
    34. parent->_right = cur->_right;
    35. }
    36. }
    37. }
    38. else if (cur->_right == nullptr)
    39. {//右为空
    40. if (cur == _root)
    41. {
    42. _root = cur->_left;
    43. }
    44. else
    45. {
    46. if (cur == parent->_left)
    47. {
    48. parent->_left = cur->_left;
    49. }
    50. else
    51. {
    52. parent->_right = cur->_left;
    53. }
    54. }
    55. }
    56. else
    57. {//左右都不为空
    58. // 右树的最小节点(最左节点)
    59. Node* parent = cur;
    60. Node* subLeft = cur->_right;
    61. while (subLeft->_left)
    62. {
    63. parent = subLeft;
    64. subLeft = subLeft->_left;
    65. }
    66. swap(cur->_key, subLeft->_key);
    67. if (subLeft == parent->_left)
    68. parent->_left = subLeft->_right;
    69. else
    70. parent->_right = subLeft->_right;
    71. }
    72. return true;
    73. }
    74. }
    75. return false;
    76. }

     删除一下看看:

    1. bt.Inorder();
    2. printf("\n");
    3. bt.Erase(3);
    4. bt.Inorder( );
    5. printf("\n");
    6. bt.Erase(14);
    7. bt.Inorder();
    8. printf("\n");

    改的话目前不能改,如我们把3改为80,那就改变搜二叉的结构了:

    托管代码

    BinarySearchTree for version ·  - 开源中国 (gitee.com)

  • 相关阅读:
    【Nginx】Nginx 鉴权 (htpasswd) + Nginx 文件下载配置
    自动化测试08
    15:00面试,15:08就出来了,问的问题有点变态。。。
    Jenkins
    Linux bash特性及bash脚本编程初步
    如果在线上遇到了OOM,该如何解决?
    yolov5调用zed相机实现三维社交距离检测(单类别)
    PDF格式分析(八十六)——修订注释(Redaction)
    「行泊一体」市场有多大?2025年前装搭载率将超40%
    web大作业:基于html+css+javascript+jquery实现智能分控网站
  • 原文地址:https://blog.csdn.net/m0_65143871/article/details/134019844