目录
[2.1 查找](#2.1 查找)
[2.2 插入](#2.2 插入)
[2.3 删除](#2.3 删除)
[1. Map.Entry,v>](#1. Map.Entry,v>)
一.二叉搜索树
1.概念
二叉搜索树具有以下特性:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值;
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值;
它的左右子树也分别为二叉搜索树;
空树也是二叉搜索树。
2.二叉搜索树功能的模拟实现
基本信息:
java
static class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
public TreeNode root ;
2.1 查找
根据二叉搜索树的特性,我们首先比较要查的值与根节点的大小关系,如果相等,正好找到;如果大于,说明可能在右子树上;如果小于,说明可能在左子树上。
这个思路很简单,直接上代码:
java
public TreeNode search(int val) {
if(root==null){
return null;
}
TreeNode cur=root;
while(cur!=null){
if(cur.val==val){
return cur;
}else if(cur.val>val){
cur=cur.left;
}else{
cur=cur.right;
}
}
return null;
}
2.2 插入
首先 我们要先查找这个点是否存在,因为这是一颗搜索树,是用来查某一个 val 是否存在的,存一个和存两个没有区别。如果不存在,说明我们可以插入了,所以说下面我们要找一个合适的位置插入。
第一步 先判断是不是空树,如果是空树直接在root上操作。如果不是就遍历这颗树,我们要不断往下找,操作类似查找。如果 一个节点比 key 的值小(说明 key 在节点的右子树上),同时这个节点的右子树还是空的,那么直接插在这个节点的右子树上;相反,如果一个节点比 key 的值大,同时左子树还是空的,那么直接插在这个节点的左子树上。
java
public void insert(int key) {
if(root==null){
root=new TreeNode(key);
return ;
}
TreeNode node=new TreeNode(key);
TreeNode cur=root;
while(cur!=null){
if(cur.val<key && cur.right==null){
cur.right=node;
}else if (cur.val>key && cur.left==null){
cur.left=node;
}else{
if(cur.val<key){
cur=cur.right;
}else if(cur.val>key){
cur=cur.left;
}else{
return ;
}
}
}
}
2.3 删除
删除操作是最难的,要考虑的情况较多。
要删除的节点可以分为以下几种情况:1.左子树是空;2.右子树是空;3.左右子树都不是空;4.左右子树都是空。
我们要定义一个 parent 节点,这个节点用来存储cur的父节点。图中的例子cur是parent的右节点,cur当然也可以是parent的左节点。

cur的左子树为空,那么要删除cur的话首先要判断cur的parent的左节点还是右节点。如果和图中例子一样为右节点,那么 parent.right = cur.right;如果为左节点,那么 parent.left = cur.right。
cur的右子树为空的判断处理与左子树的相同这里就不过多赘述了。
重量级的来的,cur的左右子树都不为空。对于这个问题,我们采用的是替换,将cur的值替换掉。
那么谁来替换cur呢? 这又要说到搜索树的性质了,cur的左子树上的值都比cur小,所以说,可以取出cur左子树中的最大值来替代cur,这样能继续满足搜索树的要求即左子树的上的值都比cur上的小(因为它是最大值)。**取cur右子树的最小值行不行?**也行。
**那什么样的节点是最值节点呢?**在左子树中,一个节点没有右子树,说明其是左子树最右面的节点,即为最大节点。同理在右子树中,一个节点没有左子树,说明其是右子树最左面的节点,即为最小节点。
知道了这些我们可以写代码了,代码上有注释:
java
public void remove(int key){
TreeNode cur=root;
TreeNode parent=root;
//找到我们要删除节点在哪里
while(cur!=null){
if(cur.val>key){
parent=cur;
cur=cur.left;
}else if(cur.val<key){
parent=cur;
cur=cur.right;
}else{
//找到了
removeNode(parent,cur);
return;
}
}
}
private void removeNode(TreeNode parent, TreeNode cur) {
if(cur.left==null){
//左子树为空
if(cur==root){
root=cur.right;
}else if(parent.left==cur){
parent.left=cur.right;
}else{
parent.right=cur.right;
}
}else if(cur.right==null){
//右子树为空
if(cur==root){
root=cur.left;
}else if(parent.right==cur){
parent.right=cur.left;
}else{
parent.left=cur.left;
}
}else{
//这里以找右子树的最小值为例
TreeNode tem=cur.right;
//存它的目的是为了一会删最值节点用的
TreeNode temParent=cur;
//找最小值所在的节点
while(tem.left!=null){
temParent=tem;
tem=tem.left;
}
cur.val=tem.val;
if(temParent.left==tem){
temParent.left=tem.right;
}else{
temParent.right=tem.right;
}
}
}
二.Map
Map是一个接口类,该类没有继承自Collection,该类中存储的是结构的键值对,并且K一定是唯一的,不能重复。
1. Map.Entry<K,V>
Map.Entry是Map的一个内部类,用来存放K和V的映射关系,也就是说它存的是一个集合,集合里都是映射关系。这个内部类里也提供了也写方法:
|---------------------|------------|
| 方法 | 功能 |
| K getKey() | 返回 key 值 |
| V getValue() | 返回 value 值 |
| V setValue(V value) | 替换 value 值 |
2.Map的常用方法
注意这里总结的是常用的方法,不是全部方法,全部方法去idea上看即可。
|--------------------------------------------|--------------------------------|
| 方法 | 功能 |
| V get(Object key) | 返回 key 对应的 value |
| V getOrDefault(Object key, V defaultValue) | 返回 key 对应的 value,key 不存在,返回默认值 |
| V put(K key, V value) | 设置 key 对应的 value |
| V remove(Object key) | 删除 key 对应的映射关系 |
| Set keySet() | 返回所有 key 的不重复集合 |
| Collection values() | 返回所有 value 的可重复集合 |
| Set<Map.Entry<K,V>> entrySet() | 返回所有的 key-value 映射关系 |
| boolean containsKey(Object key) | 判断是否包含 key |
| boolean containsValue(Object value) | 判断是否包含 value |
| void clear() | 清空集合 |
| int size() | 获得集合大小 |
| boolean isEmpty() | 判断集合是否为空 |
3.TreeMap和HashMap的区别
|------------|-----------------------------------------|------------------------------------|
| Map | TreeMap | HashMap |
| 底层结构 | 红黑树 | 哈希桶 |
| 基本操作的时间复杂度 | O() | O(
) |
| 是否有序 | 关于key有序 | 无序 |
| 线性安全 | 不安全 | 不安全 |
| 基本操作的区别 | 需要进行元素的比较 | 通过哈希实现 |
| 比较和覆写 | key必须可以比较 | 自定义类型需要覆写 |
| 应用场景 | 需要key有序场景下 | key有序无关,需要更高的时间性能 |
4.注意事项
1.Map是一个接口,接口不能直接实例化,要借助TreeMap或HashMap来实例化。
java
Map<Integer,Integer> map1=new TreeMap<>();
Map<Integer,Integer> map2=new HashMap<>();
2.key和value是一种映射关系,就是说我们可以用key来找其对应的value值。比如我们可以存姓名和年龄,姓名的key,年龄是value。我们就可以通过姓名来找到这个人的年龄。这里注意,姓名也就是key是不能重复的,但年龄(value)是可以重复的。
- TreeMap插入关系时,value可以为空 ,key不为空 ;但HashMap中都可以为空。
4.Map中的Key可以全部分离出来,存储到Set 中来进行访问(因为Key不能重复);Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)。
三.Set
Set继承自 Collection 的类接口,Set中只存储Key。
1.Set的常见方法
|--------------------------------------------|---------------------------------------|
| 方法 | 功能 |
| boolean add(E e) | 添加key进集合 |
| boolean remove(Object o) | 删除集合中的o |
| void clear() | 清空集合 |
| boolean isEmpty() | 判断集合是否为空 |
| boolean contains(Object o) | 判断集合中是否有o |
| int size() | 集合中元素的个数 |
| Iterator iterator() | 返回迭代器 |
| Object[] toArray() | 将set中的元素转换为数组返回 |
| boolean containsAll(Collection<?> c) | 集合c中的元素是否在set中全部存在,是返回true,否则返回 false |
| boolean addAll(Collection<?extends E> c) | 将集合c中的元素添加到set中,可以达到去重的效果 |
2.TreeSet和HashSet的区别
|------------|-----------------------------------------|------------------------------------|
| Set | TreeSet | HashSet |
| 底层结构 | 红黑树 | 哈希桶 |
| 基本操作的时间复杂度 | O() | O(
) |
| 是否有序 | 关于key有序 | 不一定有序 |
| 线性安全 | 不安全 | 不安全 |
| 基本操作的区别 | 需要进行元素的比较 | 通过哈希实现 |
| 比较和覆写 | key必须可以比较 | 自定义类型需要覆写 |
| 应用场景 | 需要key有序场景下 | key有序无关,需要更高的时间性能 |
3.注意事项
1.Set这个接口适用于只需要判断某一元素是否在集合里的这种情况,像判断某一班级里有没有XX这个同学。
2.TreeSet插入的key值不可以是空的 ,而HashSet可以插入空的。