栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

24.讲二叉树基础(下):有了如此高效的散列表,为什么还需要二叉树

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

24.讲二叉树基础(下):有了如此高效的散列表,为什么还需要二叉树

文章目录
  • 1. 二叉查找树(Binary Search Tree)
    • 1.1 查找
    • 1.2 插入
    • 1.3 删除
    • 1.4 二叉查找树的其他操作
  • 2.支持重复数据的二叉查找树
  • 3. 二叉查找树的时间复杂度分析
  • 4. 解答开篇

问题:既然有了这么高效的散列表,使用二叉树的地方是不是都可以替换成散列表呢?有没有哪些地方是散列表做不了,必须要用二叉树来做的呢?

1. 二叉查找树(Binary Search Tree)

又名:二叉搜索树,特点:左子树都小于节点值,右子树都大于节点值。

1.1 查找

从根节点开始,等于直接返回,比它小查左子树,比它大查右子树。以此类推。

public class BinarySearchTree {
  private Node tree;

  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 static class Node {
    private int data;
    private Node left;
    private Node right;

    public Node(int data) {
      this.data = data;
    }
  }
}
1.2 插入

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;
    }
  }
}
1.3 删除

三种情况:

public void delete(int data) {
  Node p = tree; // p指向要删除的节点,初始化指向根节点
  Node pp = null; // pp记录的是p的父节点
  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;
}
1.4 二叉查找树的其他操作

快速地查找最大节点和最小节点、前驱节点和后继节点.

重要特性:中序遍历二叉查找树,可以输出有序的数据序列,时间复杂度是O(n),非常高效。

2.支持重复数据的二叉查找树

两种处理方法:

  1. 二叉查找树中每一个节点不仅会存储一个数据,因此我们通过链表和支持动态扩容的数组等数据结构,把值相同的数据都存储在同一个节点上。
  2. 每个节点仍然只存储一个数据,如果碰到一个节点的值,与要插入数据的值相同,我们就将这个要插入的数据放到这个节点的右子树。
3. 二叉查找树的时间复杂度分析
  • 平衡二叉查找树的高度接近logn,所以插入、删除、查找操作的时间复杂度也比较稳定,是O(logn)。
  • 如果退化成链表:O(n).
4. 解答开篇
  • 散列表中的数据是无序存储的,对于二叉查找树,只需中序遍历,就可以在O(n)的时间复杂度内,输出有序的数据序列;
  • 散列表扩容耗时很多,而且当遇到散列冲突时,性能不稳定;最常用的平衡二叉查找树的性能非常稳定,时间复杂度稳定在O(logn);
  • 尽管散列表的查找等操作的时间复杂度是常量级的,但因为哈希冲突的存在,这个常量不一定比logn小,所以实际的查找速度可能不一定比O(logn)快。加上哈希函数的耗时,也不一定就比平衡二叉查找树的效率高。
  • 为了避免过多的散列冲突,散列表装载因子不能太大,特别是基于开放寻址法解决冲突的散列表,不然会浪费一定的存储空间。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/995523.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号