• 二叉搜索树的操作及其实现


    ced485cbb11e458d81a746890b32cf3f.gif 

    作者:渴望力量的土狗

    博客主页:渴望力量的土狗的博客主页

    专栏:数据结构与算法

    工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器——牛客网

    点击免费注册和我一起刷题吧

    目录

    二叉搜索树的概念:

    二叉搜索树的操作及其实现


    二叉搜索树的概念:

    二叉搜索树是一种特殊的二叉树,要么是一颗空树,要么满足以下几点:

    1、 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

    2、若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

    3、它的左右子树也分别为二叉搜索树

    下面就是一颗典型的二叉搜索树:

     

    二叉搜索树的操作及其实现

    二叉搜索树的查找效率是比较高的,类似与我们所熟知的二分查找。它的查找方式是这样的:如果我们要查找6这个数字,那么我们从根节点开始,遇到根节点的val是5,6>5,所以我们要往右搜寻,遇到7的时候,6<7,这时我们向右左找即可,这就找到了我们要找的数字。

    我们可以看的出它的时间复杂度是O(logn),也就是搜索树的高度,但是,如果这颗搜索树呈现线性的化,也就是一条线的情况:

    这时我们在搜索的时候就类似于线性查找,时间复杂度就达到了O(n)了。

    然后就是二叉搜索树的插入操作,我们怎么把数据插入到原来的二叉搜索树当中呢?插入的操作和查找有点类似,需要搜索到需要查找的位置。具体逻辑为:当树空的时候直接new一个结点即可,不空的情况下,当我们要插入一个元素的时候我们需要知道它的前一个元素,这样我们才能实现插入操作,插入的位置都是为空的,所以我们要查找到它适合的位置即可。

    二叉搜索树的删除属于最重要的内容,因为它的删除操作可能比较复杂一点,我们在删除某一个结点的时候需要调整,你怎么知道调整后是什么样子的呢?删除前我们还是要进行搜索,找到待删除结点的位置。

    这里先说一下删除的步骤:

    设待删除结点为 cur, 待删除结点的双亲结点为 parent

    一. cur.left == null (要删除结点的左边为空时)

    1. cur 是 root,则 root = cur.right

    2. cur 不是 root,cur 是 parent.left,则 parent.left = cur.right

    3. cur 不是 root,cur 是 parent.right,则 parent.right = cur.right

     

    二. cur.right == null (要删除结点的右边为空时)

    1. cur 是 root,则 root = cur.left

    2. cur 不是 root,cur 是 parent.left,则 parent.left = cur.left

    3. cur 不是 root,cur 是 parent.right,则 parent.right = cur.left

     

    三. cur.left != null && cur.right != null

    1. 需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被 删除节点中,再来处理该结点的删除问题

    下面对二叉搜索树进行实现:

    1. public class BinarySearchTree {
    2. static class TreeNode {
    3. public int key;
    4. public TreeNode left;
    5. public TreeNode right;
    6. TreeNode(int key) {
    7. this.key = key;
    8. }
    9. }
    10. public TreeNode root;
    11. /**
    12. * 插入一个元素
    13. * @param key
    14. */
    15. public boolean insert(int key) {
    16. //根结点
    17. if(root==null){
    18. root=new TreeNode(key);
    19. return true;
    20. }
    21. //找到需要插入的位置
    22. TreeNode cur=root;//找位置
    23. TreeNode parent=cur;//插入位置的父节点
    24. while(cur!=null){
    25. if(cur.key
    26. parent=cur;
    27. cur=cur.right;
    28. } else if (cur.key>key) {
    29. parent=cur;
    30. cur=cur.left;
    31. }else{
    32. //不可以插入相同的元素
    33. return false;
    34. }
    35. }
    36. //此时cur为空了,这个位置就是插入的位置
    37. //这里需要讨论插入到parent的左边还是右边
    38. if(key> parent.key){
    39. //插入右边
    40. parent.right=new TreeNode(key);
    41. }else{
    42. //插入左边
    43. parent.left=new TreeNode(key);
    44. }
    45. return true;
    46. }
    47. /**
    48. * 查找key是否存在
    49. * @param key
    50. * @return
    51. */
    52. public TreeNode search(int key) {
    53. TreeNode cur=root;
    54. //我们搜索的最差情况为当前结点为空
    55. while (cur!=null){
    56. //先判断根结点
    57. if(cur.key==key){
    58. return cur;
    59. //如果key比结点的key大向右找
    60. } else if (cur.key
    61. cur=cur.right;
    62. //如果key比结点的key小向左找
    63. } else {
    64. cur=cur.left;
    65. }
    66. }
    67. //如果cur为空则返回空
    68. return null;
    69. }
    70. //中序遍历
    71. public void inOrder(TreeNode root){
    72. if(root==null){
    73. return;
    74. }
    75. inOrder(root.left);
    76. System.out.print(root.key+" ");
    77. inOrder(root.right);
    78. }
    79. /**
    80. * 删除key的值
    81. * @param key
    82. * @return
    83. */
    84. public void remove(int key) {
    85. TreeNode parent = null;
    86. TreeNode cur = root;
    87. while (cur != null) {
    88. if(cur.key == key) {
    89. removeNode(parent,cur);
    90. return;
    91. }else if(cur.key < key) {
    92. parent = cur;
    93. cur = cur.right;
    94. }else {
    95. parent = cur;
    96. cur = cur.left;
    97. }
    98. }
    99. }
    100. private void removeNode(TreeNode parent,TreeNode cur) {
    101. if(cur.left == null) {
    102. if(cur == root) {
    103. root = cur.right;
    104. }else if(cur == parent.left) {
    105. parent.left = cur.right;
    106. }else {
    107. parent.right = cur.right;
    108. }
    109. }else if(cur.right == null) {
    110. if(cur == root) {
    111. root = cur.left;
    112. } else if (cur == parent.left) {
    113. parent.left = cur.left;
    114. }else {
    115. parent.right = cur.left;
    116. }
    117. }else {
    118. TreeNode target = cur.right;
    119. TreeNode targetParent = cur;
    120. while (target.left != null) {
    121. targetParent = target;
    122. target = target.left;
    123. }
    124. cur.key = target.key;
    125. if(target == targetParent.left) {
    126. targetParent.left = target.right;
    127. }else {
    128. targetParent.right = target.right;
    129. }
    130. }
    131. }
    132. }
  • 相关阅读:
    linux安装jenkins
    vulnhub靶机raven2
    云原生之使用Docker部署Laverna笔记工具
    vue3+TS实战中Dialog弹窗封装复用技巧(修改和添加共用一个弹窗)涉及组件的传值(defineProps)和组件的自定义事件(emits)
    CCF开源发展委员会正式成立,探索开源发展新途径
    初识Python——Python环境安装配置
    【【萌新的SOC大学习之hello_world】】
    第10章 MySQL(一)
    build g2o viewer on macos
    并发与并行,同步和异步,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang并发编程之GoroutineEP13
  • 原文地址:https://blog.csdn.net/m0_67995737/article/details/127415972