💪
AndroidCollect
  • 写在前面
  • 计算机基础
    • 计算机组成原理
    • 算法
      • 查找
        • 二分查找
      • 排序
        • 简单排序
        • 高级排序
        • 特殊排序
      • 海量数据
      • 思想
        • 贪心
        • 分治
        • 动态规划
        • 回溯
      • 哈希算法
    • 数据结构
      • 队列
        • 知识点
        • 相关题目
          • 用两个栈实现队列
          • 实现循环队列
          • 用链表实现队列
          • 用数组实现队列
      • 栈
        • 相关算法题目
          • 用链表实现栈
          • 用数组实现栈
      • 链表
        • 知识点梳理
        • 相关算法题目
          • 删除倒数第n个结点
          • 合并两个有序链表
          • 检测单链表是否有环
          • 获取中间结点
          • 反转链表
      • 跳表
      • 哈希表
      • 树
        • 二叉树
        • 二叉查找树
        • AVL 树
        • Trie 树
        • 红黑树
      • 堆
        • 存储
        • 堆的应用
      • 图
    • 网络
      • 应用层协议
        • DNS
        • HTTP
        • HTTPS
      • 传输层协议
        • TCP
        • UDP
      • 输入网址后发生了什么
    • 操作系统
      • 内存
    • 数据库
  • 软件工程
    • 编程思想
    • 设计模式
      • 状态模式
      • 装饰器模式
      • 代理模式
      • 责任链模式
      • 建造者模式
      • 单例模式
      • 观察者模式
  • Java
    • 基础
    • 异常
    • 并发编程
      • ThreadLocal
      • 线程池
      • 理解 volatile
      • AbstractQueuedSynchronizer
    • 集合
      • LinkedHashMap 源码
      • HashMap 源码
    • 注解
    • 反射
      • JDK 动态代理
    • JVM
      • 自动内存管理机制
      • Class 文件格式
      • 类加载机制
      • Java 内存模型(JMM)
      • 字节码指令
      • HotSpot 虚拟机实现细节
    • 源码与原理
    • 各版本主要特性
  • Android
    • 基础组件
      • Context
      • Activity
        • 生命周期
        • 启动模式与任务栈
        • 启动流程
      • Service
      • ContentProvider
      • BroadcastReceiver
      • Fragment
      • View
        • 常用控件问题总结
          • RecyclerView
          • ViewPager2
        • CoordinatorLayout
        • SurfaceView
        • 事件分发
        • 绘制流程
        • 自定义 View
        • Window
    • 数据存储
      • 存储结构
      • Sqlite
      • 序列化
      • SharedPreferences
    • 资源
      • 图片加载
    • 动画
      • 属性动画
    • 线程和进程
      • Binder 机制
      • 跨进程通信
        • AIDL
    • 内部原理
      • 消息循环机制
      • Binder
      • Window
      • SparseArray
      • ArrayMap
      • RecyclerView
      • App 启动流程
    • 性能优化
      • 内存
        • 内存使用优化
        • 内存泄漏
      • 启动优化
      • 缩减包大小
      • 布局优化
      • ANR
    • 打包构建
      • dex 文件
      • APK 打包流程
      • APK 签名流程
    • 架构
      • 运行时
      • Android 系统架构
      • 应用项目架构
    • 开源框架源码或原理
      • RxJava
        • 使用笔记
        • 源码解析
      • Retrofit
      • ButterKnife
      • BlockCanary
      • LeakCanary
      • OkHttp
      • 图片加载
        • Glide
        • Picasso
    • 碎片化处理
      • 屏幕适配
    • 黑科技
      • 热修复
    • Jetpack
      • Lifecycle
      • Room
      • WorkManager
    • 新动态
      • AndroidX
      • 各系统版本特性
  • 开发工具
    • 正则表达式
    • ADB
    • Git
  • Kotlin
  • Flutter
  • 关于作者
  • 致谢
由 GitBook 提供支持
在本页
  • 概念
  • 查找
  • 插入
  • 删除
  • 重复元素
  • 与哈希表对比

这有帮助吗?

  1. 计算机基础
  2. 数据结构
  3. 树

二叉查找树

上一页二叉树下一页AVL 树

最后更新于5年前

这有帮助吗?

概念

也叫二叉搜索树,对于树中每个节点,左子树的每个节点的值都小于这个节点的值,而右子树的每个节点的值都大于这个节点的值。

中序遍历二叉查找树可以得到有序序列。

查找

从根节点开始遍历,如果值小于指定值 ,那么从右子树继续搜索,否则从左子树进行搜索。

public Node find(int data) {    
    Node p = tree;    
    while (p != null) {      
        if (data < p.data) {
            p = p.left;      
        }
        else if (data > p.data){
            p = p.right;
        }      
        else {
            return p;
        }    
    }    
    return null;  
}

插入

根据大小关系找到对应的父节点,然后插入。

public void insert(int data) { 
    if (tree == null) { 
        tree = new Node(data); 
        return; 
    } 
    Node p = tree; 
    while (p != null) { 
        if (data > p.data) { 
            if (p.right == null) { 
                p.right = new Node(data); 
                return; 
            } 
            p = p.right; 
        } else { // data < p.data
            if (p.left == null) { 
                p.left = new Node(data); 
                return; 
            } 
            p = p.left; 
        } 
    }
}

删除

  • 如果要删除的节点没有子节点,我们只需要直接将父节点中,指向要删除节点的指针置为 null。

  • 如果要删除的节点只有一个子节点(只有左子节点或者右子节点),我们只需要更新父节点中,指向要删除节点的指针,让它指向要删除节点的子节点就可以了。

  • 如果要删除的节点有两个子节点,这就比较复杂了。需要找到这个节点的右子树中的最小节点(或者左子树的最大结点),把它替换到要删除的节点上。然后再删除掉这个最小节点,因为最小节点肯定没有左子节点(如果有左子结点,那就不是最小节点了),所以,我们可以应用上面两条规则来删除这个最小节点。

public void delete(int data) {
  Node p = tree; // p 指向要删除的节点,初始化指向根节点
  Node pp = null; // pp 记录的是待删除节点的父节点
  while (p != null && p.data != data) {
    pp = p;
    if (data > p.data){
        p = p.right;
    } else {
        p = p.left;
    }
  }
  if (p == null) {
      return; // 没有找到
  }

  // 要删除的节点有两个子节点
  if (p.left != null && p.right != null) { // 查找右子树中最小节点
    Node minP = p.right;
    Node minPP = p; // minPP表示minP的父节点
    while (minP.left != null) {
      minPP = minP;
      minP = minP.left;
    }
    p.data = minP.data; // 将minP的数据替换到p中
    p = minP; // 下面就变成了删除minP了
    pp = minPP;
  }

  // 删除节点是叶子节点或者仅有一个子节点
  Node child; // p的子节点
  if (p.left != null){
      child = p.left;
  }  else if (p.right != null){
      child = p.right;
  } else {
      child = null;
  }

  if (pp == null) tree = child; // 删除的是根节点
  else if (pp.left == p) pp.left = child;
  else pp.right = child;
}

重复元素

可以把重复元素当成比它大的元素插入,查找时找到第一个后继续遍历右子树,这样可以把所有等于某个值的所有节点都找出来,代码实现也比较简单。

与哈希表对比

  • 中序遍历即可得到有序数据,而哈希表需要再排序

  • 哈希表存在扩容问题,并且冲突时性能不稳定,而平衡的二叉查找树可以把时间复杂度稳定在O(logn)

  • 哈希表构造比较复杂,要考虑哈希函数、扩容、哈希冲突等,而二叉查找树实现起来比较简单

二叉查找树示例