1️⃣ 特点
- 🎯 特点:
- 📎 有明确的根节点
- 📎 每个节点最多有两个子节点
- 📎 用于高效的查找、插入、删除操作。
- 📎 有许多特殊的子类,如满二叉树、完全二叉树、平衡二叉树等
- 🔍 结构: 由节点组成,每个节点包含一个数据元素,一个指向左子节点的指针和一个指向右子节点的指针。
2️⃣ Java中常用的二叉树
📌 二叉搜索树 (Binary Search Tree
)
-
🎯 特点:
- 📎 对于树中的每个节点,其左子树的所有节点的值都小于该节点的值
- 📎 对于树中的每个节点,其右子树的所有节点的值都大于该节点的值
- 📎 提供了
O(log n)
的查找、插入和删除操作时间复杂度(在平衡的情况下) - 📎 如果不平衡,最坏情况下的时间复杂度可能达到
O(n)
-
🔍 结构:
- 二叉搜索树是一个节点结构,其中每个节点包含一个键值和两个子节点的引用,通常称为左子节点和右子节点。
-
🛠️ 使用场景:
- 📎 动态数据结构中的查找、插入和删除操作
- 📎 构建有序的列表或集合
- 📎 实现某些算法,如树排序算法
-
⚠️ 注意: 为了保持
O(log n)
的操作效率,当进行多次插入和删除操作后,通常需要对二叉搜索树进行平衡。有许多自平衡的二叉搜索树变种,如红黑树、AVL树等。
📌 平衡二叉搜索树 (Balanced Binary Search Tree
)
-
🎯 特点:
- 📎 是二叉搜索树的一种特殊形式。
- 📎 任何节点的两个子树的高度差最多为1。
- 📎 通过旋转操作来自动保持平衡。
- 📎 保证了
O(log n)
的查找、插入和删除操作时间复杂度。
-
🔍 结构:
- 平衡二叉搜索树维持了基本的二叉搜索树结构,但添加了自平衡机制。每次插入或删除节点后,树会自动检查自己是否仍然平衡。如果不平衡,它会执行一系列的旋转来重新平衡自己。
-
🛠️ 使用场景:
- 📎 动态数据结构中的查找、插入和删除操作,尤其在数据频繁变动的情况下。
- 📎 数据库和文件系统的实现,其中平衡和查找效率至关重要。
- 📎 实现优先队列、排序算法等。
-
⚠️ 注意: 有多种平衡二叉搜索树的实现,如AVL树、红黑树等。每种实现都有其自己的平衡条件和旋转操作。
📌 完全二叉树 (Complete Binary Tree
)
-
🎯 特点:
- 📎 除了最后一层外,所有层次的节点都是满的,且最后一层的节点都集中在左侧。
- 📎 第 (i) 个节点的左孩子是第 (2i) 个节点,右孩子是第 (2i+1) 个节点(如果存在)。
- 📎 完全二叉树的高度为 (O(\log n))。
-
🔍 结构:
-
完全二叉树通常用数组来表示,因为这种结构使得父节点和孩子节点之间的关系容易确定。例如,给定一个父节点的索引 (i),你可以很容易地找到它的左孩子 ((2i)) 和右孩子 ((2i+1))。
-
-
🛠️ 使用场景:
- 📎 用于实现二叉堆,进而实现优先队列。
- 📎 通常用于需要快速、定量访问的数据结构,如堆排序。
-
⚠️ 注意: 完全二叉树不同于满二叉树。在满二叉树中,每一层的所有节点都是满的。完全二叉树只要求除了最后一层外的其他层都是满的,且最后一层的节点都靠左排列。
📌 满二叉树 (Full Binary Tree
)
-
📜 定义: 满二叉树是一个二叉树,其中每个节点都有0个或2个子节点。
-
🎯 特点:
- 📎 没有只有一个子节点的节点。
- 📎 所有的叶子节点都在同一层。
- 📎 对于任何非叶子节点,它必须有两个子节点。
- 📎 如果一个满二叉树的高度为 ( h ),那么它最多有 ( 2^h - 1 ) 个节点。
-
🔍 结构:
-
满二叉树是一个特殊的二叉树,其中每个节点都有两个子节点或没有子节点。
-
-
⚠️ 注意: 请不要将满二叉树与完全二叉树或平衡二叉树混淆。这些是不同的概念,每种树都有其特定的性质和定义。
📌 红黑树 (Red-Black Tree
)
-
🎯 特点:
- 📎 每个节点都有颜色属性,红色或黑色
- 📎 根节点总是黑色的
- 📎 所有叶子节点(通常是
NIL
或空节点)都是黑色 - 📎 如果一个节点是红色的,则它的两个子节点都是黑色的(没有两个连续的红色节点)
- 📎 对于每个节点,从该节点到其所有后代叶子节点的任何路径都包含相同数量的黑色节点
- 📎 插入、删除和查找操作的时间复杂度为
O(log n)
-
🎨 颜色解释:
- 🟥 红色:
- 在红黑树中,红色节点提供了一种机制来允许树在插入和删除时适应变化,从而保持平衡。
- 红色节点不能连续出现,即一个红色节点的子节点必须是黑色的。
- ⬛ 黑色:
- 黑色节点是红黑树中的主要结构,确保所有从根到叶的路径有相同数量的黑色节点,这保证了最长路径不会超过最短路径的两倍。
- 🟥 红色:
-
🔍 结构:
红黑树是一个自平衡的二叉查找树,其中每个节点包含一个额外的位来存储颜色信息(红色或黑色)。这些颜色用于确保树在插入和删除操作后仍然大致平衡,从而确保O(log n)
的查找时间。 -
🛠️ 使用场景:
- 📎 动态数据结构中的查找、插入、删除操作,如语言库中的集合和映射数据结构
- 📎 用于数据库中的索引结构,以确保平衡和快速的查找、插入、删除操作
- 📎 当需要一个平衡二叉搜索树时,红黑树是一个常用的选择,因为它的插入和删除操作相对简单并且能够快速地重新平衡
- ⚠️ 注意: 虽然AVL树在查找操作中可能比红黑树更快(因为它是更严格地平衡的),但红黑树的插入和删除操作通常更快,并且需要更少的旋转
📌 堆 (Heap
)
-
📜 定义: 堆是一个可以在 (O(\log n)) 时间内进行插入和删除操作的特殊树结构。
-
🎯 特点:
- 📎 堆是一个完全二叉树。
- 📎 堆的常见实现是二叉堆,但也可以是三叉、四叉等。
- 📎 最大堆:任何节点的值都大于或等于其子节点的值。
- 📎 最小堆:任何节点的值都小于或等于其子节点的值。
-
🔍 结构:
- 📈 最大堆: 用于实现“最大优先队列”。
- 📉 最小堆: 用于实现“最小优先队列”。
-
🛠️ 使用场景:
- 📎 用于实现优先队列,如操作系统中的任务调度、网络路由算法等。
- 📎 堆排序算法中的关键数据结构。
- 📎 在求解最小生成树的算法中,如 Prim’s 和 Kruskal’s 算法。
- 📎 用于求解单源最短路径问题的算法,如 Dijkstra’s 算法。
📌 B树 (B-Tree
)
-
🎯 特点:
- 📎 B树是一种自平衡的树,能保持数据有序并允许搜索、顺序访问、插入和删除操作都在对数时间内完成。
- 📎 节点可以有多个子节点,数量通常介于2到
M
之间,其中M
是树的阶。 - 📎 树的所有叶节点都在同一层。
- 📎 为磁盘存储系统优化:通过减少I/O操作数量来读取或查找键。
-
🔍 结构:
- B树中的每个节点包含n个键和n+1个子节点的引用。键在节点内部按升序排列。
-
🛠️ 使用场景:
- 📎 数据库和文件系统中使用,因为它们对磁盘I/O操作的数量非常敏感。
- 📎 当数据不能完全加载到主存储器时使用。
-
⚠️ 注意: B树的插入和删除操作可能会导致节点分裂或合并。为了维持B树的属性,这些操作可能会递归地向上或向下到树的其他部分。
📌 B+树 (B+-Tree
)
-
🎯 特点:
- 📎 B+树是B树的一个变种,用于数据库和文件系统。
- 📎 在B+树中,只有叶节点保存数据记录,内部节点仅用于导航。
- 📎 叶节点之间通过指针相互连接,这使得范围查询更加高效。
- 📎 与B树不同,B+树的所有键值都会出现在叶节点。
-
🔍 结构:
-
B+树的每个内部节点包含n个键和n+1个子节点的引用。所有的叶节点都在同一层,并通过指针相互连接。
-
-
🛠️ 使用场景:
- 📎 数据库索引,特别是当需要执行范围查询时。
- 📎 文件系统。
-
⚠️ 注意: 尽管B+树在某些方面比B树更高效(特别是对于范围查询),但B+树的插入和删除可能会比B树更复杂,因为它们需要处理叶节点之间的链接。
3️⃣ Java中二叉树的使用
📌 TreeSet (TreeSet
)
-
📜 定义:
TreeSet
是基于红黑树 (一种自平衡二叉搜索树) 的实现的一个有序集合。它是 Java 的java.util
包中的一部分。 -
🎯 特点:
- 📎
TreeSet
中的元素是唯一的,不允许重复。 - 📎 元素在
TreeSet
中自动排序(按自然顺序或根据提供的比较器)。 - 📎 插入、删除和查找操作的时间复杂度为 (O(\log n))。
- 📎 提供了丰富的导航方法,如
first()
,last()
,lower()
,higher()
等。 - 📎 不同于
HashSet
,TreeSet
保证了元素的顺序。
- 📎
-
🔍 结构:
- 内部基于红黑树实现,确保了数据的有序性和平衡性。
-
🛠️ 使用场景:
- 📎 当需要一个不允许重复元素的有序集合时。
- 📎 当需要快速查找、删除和插入操作,同时又需要元素有序时。
- 📎 在需要导航方法(如获取一个集合中的最小或最大元素)时。
⚠️ 注意: 为了插入到 TreeSet
中,元素必须是可比较的(实现 Comparable
接口)或者需要提供一个比较器 (Comparator
)。
// 创建一个新的 TreeSet
TreeSet<String> fruits = new TreeSet<>();
// 向 TreeSet 添加元素
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");
fruits.add("Date");
fruits.add("Elderberry");
// 输出 TreeSet
System.out.println("Fruits Set: " + fruits);
// 检查特定元素是否存在
System.out.println("Contains Banana? " + fruits.contains("Banana"));
// 删除元素
fruits.remove("Date");
System.out.println("After removing Date: " + fruits);
// 获取并输出第一个和最后一个元素
System.out.println("First fruit: " + fruits.first());
System.out.println("Last fruit: " + fruits.last());
// 获取并输出比给定元素小的最大元素
System.out.println("Fruit before Cherry: " + fruits.lower("Cherry"));
// 获取并输出比给定元素大的最小元素
System.out.println("Fruit after Cherry: " + fruits.higher("Cherry"));
📌 TreeMap (TreeMap
)
-
📜 定义:
TreeMap
是一个基于红黑树的实现的键值对映射。它是 Java 的java.util
包中的一部分。 -
🎯 特点:
- 📎
TreeMap
中的键是唯一的,不允许重复。 - 📎 键在
TreeMap
中自动排序(按自然顺序或根据提供的比较器)。 - 📎 插入、删除和查找操作的时间复杂度为 (O(\log n))。
- 📎 提供了丰富的导航方法,如
firstKey()
,lastKey()
,lowerKey()
,higherKey()
等。 - 📎 不同于
HashMap
,TreeMap
保证了键的顺序。 - 📎 可以根据键或键的范围获取子映射。
- 📎
-
🔍 结构:
- 内部基于红黑树实现,确保了数据的有序性和平衡性。
-
🛠️ 使用场景:
- 📎 当需要一个键值对映射,且键需要有序时。
- 📎 当需要快速查找、删除和插入操作,同时又需要键有序时。
- 📎 在需要导航方法(如获取一个映射中的最小或最大键)或子映射时。
⚠️ 注意: 为了插入到 TreeMap
中,键必须是可比较的(实现 Comparable
接口)或者需要提供一个比较器 (Comparator
)。
// 创建一个新的 TreeMap
TreeMap<String, Integer> fruitPrices = new TreeMap<>();
// 向 TreeMap 添加元素
fruitPrices.put("Apple", 100);
fruitPrices.put("Banana", 50);
fruitPrices.put("Cherry", 200);
fruitPrices.put("Date", 75);
fruitPrices.put("Elderberry", 150);
// 输出 TreeMap
System.out.println("Fruit Prices: " + fruitPrices);
// 获取特定键的值
System.out.println("Price of Banana: " + fruitPrices.get("Banana"));
// 删除键值对
fruitPrices.remove("Date");
System.out.println("After removing Date: " + fruitPrices);
// 获取并输出第一个和最后一个键
System.out.println("First fruit: " + fruitPrices.firstKey());
System.out.println("Last fruit: " + fruitPrices.lastKey());
// 获取并输出比给定键小的最大键
System.out.println("Fruit before Cherry: " + fruitPrices.lowerKey("Cherry"));
// 获取并输出比给定键大的最小键
System.out.println("Fruit after Cherry: " + fruitPrices.higherKey("Cherry"));
4️⃣ Java中的常用场景
- 数据库中的索引经常使用 B-Tree 或 B+Tree(这些是多路搜索树,是二叉树的一种推广)。
- 用于排序的
TreeMap
和TreeSet
。 - 优先队列通常使用二叉堆来实现。
5️⃣ 工具类的使用
Google Guava
:
Google Guava库提供了一些非常有用的数据结构和工具,对于二叉树相关的数据结构,以下是一些关键的类:
TreeMultiset
:- 这是一个有序的多集合,允许重复的元素。
- 它在内部使用一个平衡的二叉搜索树。
- 常用于统计和排序。
TreeMultiset<Integer> multiset = TreeMultiset.create();
multiset.add(10);
multiset.add(10);
multiset.add(20);
int count = multiset.count(10); // 返回2,因为10添加了两次
TreeMultimap
:- 这是一个可以保持键的排序,并允许重复键值对的映射。
- 每个键都可以映射到多个值。
TreeMultimap<String, Integer> multimap = TreeMultimap.create();
multimap.put("a", 10);
multimap.put("a", 20);
Collection<Integer> values = multimap.get("a"); // 返回[10, 20]
Apache Commons Collections
:
Apache Commons Collections是一个提供大量集合工具和数据结构的库。对于二叉树,Java的标准库提供的数据结构通常已经足够。然而,Apache Commons Collections为其他许多数据结构提供了强大的支持,如BidiMap, MultiMap等。
⚠️ 注意: 当考虑使用外部库时,最好先了解Java的标准库,因为它通常提供了大多数常见的数据结构和算法。如果标准库不满足需求,那么可以考虑使用像Guava这样的第三方库。