上篇文章【Java】Map & Set_p_fly的博客-CSDN博客 介绍了Map和Set这两个接口,其中也提到了TreeMap和TreeSet,接下来就详细介绍一下这两个类。
TreeMap和TreeSet的底层是一个红黑树,红黑树是由AVL树的基础上做的升级,而二叉搜索树改变一下就变成了一个AVL树。所以了解一下二叉搜索树对于理解TreeMap和TreeSet有很大的帮助。
二叉搜索树又叫二叉排序树,它首先是一个二叉树,其次它的左子树比根小,右子树比根大,左子树和右子树可以为空,最后在这颗树中,没有相同的值。它的中序遍历是有序的。
增加结点:
1.当树为空的时候,直接增加一个结点,让根的引用指向它即可。
2.当树不空的时候,增加的结点就是要到叶子结点的位置。增加结点的值和根结点的值比较,小了去左边,大了去右边。所以定义一个工作指针指向根节点,从根结点出发找合适的插入位置。
注意:当找到合适的叶子节点位置后,此时如何插入结点,无法找到这个叶子结点的父亲 结点。所以就要再有一个指针一直指向工作结点的父亲结点。
public boolean insert(int key) {
if (root == null) {
TreeNode node = new TreeNode(key);
root = node;
}
else {
TreeNode node = new TreeNode(key);
TreeNode curPrev = null;
TreeNode cur = root;
while (cur != null) {
curPrev = cur;
if (key > cur.key) {
cur = cur.right;
} else if (key < cur.key) {
cur = cur.left;
} else {
return false;
}
}
if (curPrev.key < key) {
curPrev.right = node;
} else {
curPrev.left = node;
}
}
return true;
}
查找结点:
定义一个工作指针,从根节点出发,大了走左边,小了走后边,相等了就是找到了,走完了还没找到就是没有。
public TreeNode search(int key) {
if (root == null) {
return null;
}
else {
TreeNode cur = root;
while (cur != null) {
if (cur.key < key) {
cur = cur.right;
} else if (cur.key > key) {
cur = cur.left;
} else {
return cur;
}
}
}
return null;
}
结点删除:
删除整体分为三种情况。
一:当要删除的结点右子树为空
①当该结点为根结点时
②不是根结点。在要被删除结点的父亲结点的左边
③不是根结点。在要被删除结点的父亲结点的右边
二: 当要删除的结点的左子树为空
①当该结点为根节点时
②不是根结点。在要被删除结点的父亲结点的左边
③不是根结点。在要被删除结点的父亲结点的右边
三:当要删除的结点的左右子树都不为空时
找到该结点左子树最右边的结点,交换值后删除最右边的那个结点,步骤同上。
或者找到该结点右子树最左边的结点,交换值后删除最左边的那个结点,步骤同上。
public boolean remove(int key) {
if (root == null) {
return false;
}
else {
TreeNode curPrev = null;
TreeNode cur = root;
while (cur != null && cur.key != key) {
curPrev = cur;
if (cur.key < key) {
cur = cur.right;
} else if (cur.key > key) {
cur = cur.left;
} else {
break;
}
}
if (cur == null) {
return false;
}
//此时找到了要删的值
//一:当cur.left == null
if (cur.left == null) {
if (cur == root) {
root = root.right;
} else if (cur == curPrev.left) {
curPrev.left = cur.right;
} else {
curPrev.right = cur.right;
}
}
//二:当cur.right == null
else if (cur.right == null) {
if (cur == root) {
root = root.left;
} else if (cur == curPrev.left) {
curPrev.left = cur.left;
} else {
curPrev.right = cur.left;
}
}
//三:当cur.left != null && cur.right != null
//替换法:找到左边中最后边的交换 或者 找到右边中最左边的交换
//找到那个后,然后又是第一或第二中情况了
else {
TreeNode targetPrev = cur;
TreeNode target = cur.left;
//找左边的最右边
while (target.right != null) {
targetPrev = target;
target = target.right;
}
cur.key = target.key;
//一种是target没有走 就是target的右边没有
if (targetPrev.left == target) {
targetPrev.left = target.left;
}
//一种是target走了
else {
targetPrev.right = target.left;
}
}
}
return true;
}
TreeMap是继承Map接口的,基本方法和性质与Set都一样。
增加的Key必须是可比较——实现Comparable接口或者Comparator接口
原因很容易想到,它的底层是一颗红黑树,树必须得能比较才可以建树。
import java.util.TreeMap;
class Student {
public int age;
public String name;
public Student(int age){
this.age = age;
}
}
public class TreeTest {
public static void main(String[] args) {
TreeMap treeMap1 = new TreeMap<>();
Student student = new Student(20);
treeMap1.put(student, 5);
}
}
如果没有实现,那么就会报出以上错误。实现接口即可
import java.util.TreeMap;
class Student implements Comparable {
public int age;
public String name;
public Student(int age){
this.age = age;
}
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
}
public class TreeTest {
public static void main(String[] args) {
TreeMap treeMap1 = new TreeMap<>();
Student student = new Student(20);
treeMap1.put(student, 5);
}
}
TreeMap的Key不可以增加null
HashMap可以。
TreeMap treeMap2 = new TreeMap<>();
treeMap2.put(null, 5);
TreeMap总结
TreeMap 底层结构
|
红黑树 |
插入 / 删除 / 查找时间
复杂度
|
log₂N |
是否有序
|
关于 Key 有序
|
线程安全
|
不安全 |
应用场景 | 需要Key有序 |
TreeSet的注意事项与MapSet一样。因为它是实现了Set接口的类,基本方法性质都与Set一致。