日升时奋斗,日落时自省
目录
一、Map和Set
1、搜索树的基本概念
2、二叉搜索查找
3、二叉搜索树插入
4、二叉搜索树删除
二、TreeMap
三、TreeSet
Map/Set 及实际实现类 HashMap/TreeMap/HashSet/TreeSet 的使用,本次复习TreeMap和TreeSet
Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关:
1.直接遍历,时间复杂度为O(N),元素如果比较多效率会非常慢
2. 二分查找,时间复杂度为O( ),但搜索前必须要求序列是有序的
放入现实中都很难遇到巧合的情况,集合就更针对实现的复杂情况,动态查找集合便是更优的选择Map和Set就是动态查找容器
TreeMap是一个搜索树,但是不仅仅是一颗搜索树,因为单单的搜索树是不能实现很多情况为最优
搜索树相对于TreeMap其他底层代码更简单,初步入手
二叉搜索树也叫二叉排序树,基本性质:
左子树所有节点的值都小于根节点的值;右子树所有节点的值都大于根节点的值;
它的左右子树都遵循前面的条件,分别也会构成二叉搜索树,这一整棵树才算是二叉搜索树
用一个数据举例:5 3 4 1 7 8 2 6 0 9
二叉搜索树的查找有点类似于二分法查找,每次找的数据都会与当前根的值进行比较,大于根节点的就向根的右边进行查找,小于根节点的就向左边进行查找
借上面的图
思路解析:
(1)一颗搜索树不为空情况下
(2)节点值==key,return true;
(3)节点值>key,向左子树找
(4)节点值
(5)key在二叉搜索树中不存在,return false
图解:
(1)空树
(2)非空书
static class NodeTree{
public int val;
public NodeTree left;
public NodeTree right;
public NodeTree(int val){
this.val=val;
}
}
public static NodeTree root=null;
public boolean search(int key){
NodeTree cur=root;
while(cur!=null){ //树不为空
if(cur.val>key) { //节点值>key,向左子树找
cur=cur.left;
}else if(cur.val
开始已经知道二叉树是什么了,插入其实就比较简单了
思路:
(1)树为空的情况,直接插入 (树为空)
(2)比根节点小,向左子树走 (树不为空)
(3)比根节点大,向右子树走
(4)如果找到一样的值了,就返回false
(5)直到走到空,就可以插入了
注:找到空了,怎么插入呢,这里就需要一个知道他的父节点,所以这里要记录一个父节点
(1)空树
代码解析:
public boolean insert(int key){ //二叉搜索树进行插入
if(root==null){
root=new NodeTree(key); //如果为空的话就只插入
return true;
}
NodeTree cur=root;
NodeTree parent=null; //这里记录一下父节点,为了后面找到插入位置,进行连接
while(cur!=null){ //树不为空,一直找
if(cur.val>key){ //跟节点值小的,
parent=cur; //记录当前父节点
cur=cur.left; //找右子树
}else if(cur.val parent.val){ //比父亲节点小,左边插入
parent.right=node;
}else{ //比父亲节点大,右边插入
parent.left=node;
}
return true;
}
删除的步骤就相对较多了
思路: 分析以下情况 (设置待删除节点为cur,当前父节点为parent)
(1)cur.left==null
(2)cur.right==null
(3)cur.left!=null&&cur.right!=null
以上三种情况,我们通过图解来详细解释
用替换法将待删除的值替换,并且保证整个二叉搜索树不会动摇
图解:
特殊情况:
代码解析:
代码是与图是相反的可以看着图来推当前代码
public void removeNode(NodeTree parent,NodeTree cur){
if(cur.left==null){ //为了找到待删除的值
if(cur==root){
root=cur.right;
}else if(cur==parent.left){
parent.left=cur.right;
}else if(cur==parent.right){
parent.right=cur.right;
}
}else if(cur.right==null){
if(cur==root){
root=cur.left;
}else if(cur==parent.left){
parent.left=cur.left;
}else if(cur==parent.right){
parent.right=cur.left;
}
}else{ //这里我们取一种就可以了
NodeTree target=cur.right;
NodeTree targetparent=cur;
while(target.left!=null) { //为了找替换值
targetparent=target;
target=target.left;
}
cur.val=target.val;
if(target==targetparent.left){ //分两种情况
targetparent.left=target.right;
}else{
targetparent.right=target.right;
}
}
}
里面是一个集合 ,TreeMap的底层代码是一个Map集合接口
Map底层结构 | TreeMap | |
底层结构 | 红黑树 | |
插入/删除/查找时间 复杂度 |
O(1) | |
是否有序 | 关于Key有序 | |
线程安全 | 不安全 | |
插入/删除/查找区别 | 需要进行元素比较 | |
比较与覆写 | key必须能够比较,否则会抛出 ClassCastException异常 |
|
应用场景 | 需要Key有序场景下 |
泛型有两个第一个传入key,第二个传入value
这里的key是可以比较的key值,传key时调用了比较器,如果不是可以比较的,就会报错
put方法就是将我们需要的key,value值放入存储起来
getOrDefault方法是key是否找到,如果没有就返回默认值
除了上面的方法,还有以下比较基本的方法使用,可以尝试
方法 | 解释 |
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 |
返回所有 key 的不重复集合 |
Collection |
返回所有 value 的可重复集合 |
Set |
返回所有的 key-value 映射关系 |
boolean containsKey(Object key) | 判断是否包含 key |
boolean containsValue(Object value) | 判断是否包含 value |
前面的图TreeMap用Set棘手后打印
那Map的打印应怎么处理呢
这里肯定不能用foreach,迭代打印因为都没有继承关系
代码解释:
public static void main(String[] args) {
Map map=new TreeMap<>();
map.put("hello",2);
map.put("abc",1);
Set> entries=map.entrySet();
for (Map.Entry set:entries) {
System.out.println(set.getKey()+" "+set.getValue());
}
}
图解一下:
TreeSet的底层是TreeMap,去掉重复的元素,key的重复不会再搜索树中出现
TreeSet继承了Set,Set又继承了Collection
注:将Map与Set联系起来Set
> entries=map.entrySet(); 才能使用Iterable中迭代打印,foreach打印等方法,Map一系中是独立出来并没有实现在下面的关系图中,TreeSet弥补了当前的缺陷。
TreeSet的底层是TreeMap,直接将TreeMap与上面的关系图联系起来,TreeSet也同样有TreeMap的限制,泛型传值中是需要经过比较。(用于Map相同的方法)
Set
> entries=map.entrySet(); 解释: