数据结构_Map和Set

数据结构------Map和Set

一、搜索树

1.1概念

二叉搜索树又称为二叉排序树,它或者是一棵空树,或者

  • 左子树不为空则左子树所有节点的值小于根节点
  • 右子树不为空则右子树所有节点的值小于根节点
  • 左右子树也分别为二叉搜索树

1.2操作-查找

1.3操作-插入

1.4操作-删除

java 复制代码
 public void remove( int val) {
        if (root == null) {
            return;
        }
        TreeNode cur = root;
        TreeNode parent = root;
        while (cur != null) {
            if (val < cur.val) {
                parent = cur;
                cur = cur.left;
            } else if (val > cur.val) {
                parent = cur;
                cur = cur.right;
            } else {
                delete(cur, parent);
                return;
            }
        }
    }

    private void delete(TreeNode cur, TreeNode parent) {
        //cur的左子树为空
        if (cur.left == null) {
            if (cur == root) {
                root = null;
            } else if (cur == parent.left) {
                parent.left = cur.right;
            } else {
                parent.right = cur.right;
            }
        }
        //cur的右子树为空
        if (cur.right == null) {
            if (cur == root) {
                root = null;
            } else if (cur == parent.right) {
                parent.right = cur.left;
            } else {
                parent.left = cur.left;
            }
        }
        //cur的左右子树都不为空
        if (cur.left != null && cur.right != null) {
            TreeNode tp = cur.right;
            TreeNode t = tp;
            while (t.left != null) {
                tp = t;
                t = t.left;
            }
            cur.val = t.val;
            if (t.right == null) {
                tp.left = null;
            } else {
                if (tp.left == t) {
                    tp.left = t.right;
                } else {
                    tp.right = t.right;
                }
            }
        }
    }

1.6性能分析

最优情况下,二叉树为完全二叉树,其平均比较次数为log2N

最差情况下,为单支树,其平均比较次数为N/2

1.7与java类集的关系

TreemMap和TreeSet即java中利用搜索树实现的Map和Set,实际上用的是红黑树,而红黑树是一棵近似平衡的二叉搜索树,即在二叉搜索树的基础上+颜色以及红黑树 性质验证,关于红黑树内容后续进行详解。

二、搜索

2.1概念

Map和Set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其实例化的子类有关,以前常见的搜索方式有:

1.直接遍历,O(N),元素多则效率慢

2.二分查找,O(log2N),但搜索前必须有序

上面的查找适合静态查找,不会对区间进行插入和删除了,但现实中例如根据姓名查询成绩,可能查找时要进行插入和删除操作,即动态查找,那么Map和Set是一种适合动态查找的集合容器。

2.2模型

Map:Key-Value模型

Set:纯Key模型

三、Map的使用

3.1概念

Map是一个接口类,该类没有继承Collection,该类中存储的是<K,V>结构的键值对,K是唯一不能重复

3.2Map.Entry<K,V>

Map.Entry<K,V>是Map内部实现的用来存放<key,value>键值对映射关系的内部类

方法 解释
K getKey() 返回entry中的key
V getValue() 返回entry中的value
V setValue(V value 将键值对中的value替换为指定value
  • Map.Entry<K,V>没有提供设置Key的方法

3.3Map常用方法的使用

方法 解释
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

注意:

  • Map是一个接口,不能直接实例化对象,如果要实例化对象,只能实例化其实现类TreeMap或者HashMap
  • Key是唯一的,value是可以重复的
  • TreeMap中插入键值对是,Key不能为空Value可以
  • Map中的Key值可以全部分离出来,存储在Set中进行访问(因为Key不能重复)
  • Map中的Value值可以全部分离出来,c存储在Collection的任何一个子集中(Value可能重复)
  • Map中键值对的Key不能直接修改,value可以修改,如果要修改key,必须删除原有key
  • TreeMap与HashMap的区别
Map底层结构 TreeMap HashMap
底层结构 红黑树 哈希桶
插入/删除/查找时间复杂度 (O(log2N)) (O(1))
是否有序 关于Key有序 无序
线程安全 不安全 不安全
插入/删除/查找区别 需要进行元素比较 通过哈希函数计算哈希地址
比较与覆写 key必须能够比较,否则会抛出ClassCastException异常 自定义类型需要覆写equals和hashCode方法
应用场景 需要Key有序场景下 Key是否有序不关心,需要更高的时间性能

3.4TreeMap的使用案例

java 复制代码
public static void TestMap(){
    Map<Integer,String> map= new TreeMap<>();
        map.put(1,"a");
        map.put(2,"b");
        map.put(3,"c");
        map.put(4,"d");
        map.put(5,"e");
        System.out.println(map);
        //value可为空
        map.put(6,null);
        System.out.println(map.get(5));
        System.out.println(map.get(6));
        //key存在返回对应的value不存在返回defaultValue
        System.out.println(map.getOrDefault(5,null));
        System.out.println(map.getOrDefault(7,"h"));
        //检测key是否存在
        System.out.println(map.containsKey(7));
        System.out.println(map.containsKey(4));
       // 检测key是否存在
        System.out.println(map.containsValue("a"));
        System.out.println(map.containsValue("A"));
        //打印所有的key,KeySet()返回所有key的不重复集合
        for(int k:map.keySet()){
            System.out.println(k);
        }
        System.out.println();
        //打印所有value,values()返回所有value的可重复集合
        for(String s:map.values()){
            System.out.println(s);
        }
        System.out.println();
        //打印所有键值对
        for(Map.Entry<Integer,String> entry:map.entrySet()){
            System.out.println(entry.getKey()+"->"+entry.getValue());
        }
        System.out.println();
    }

运行结果:

四、Set的说明

4.1常见方法

方法 解释
boolean add(E e) 添加元素,但重复元素不会被添加成功
void clear() 清空集合
boolean contains(Object o) 判断 o 是否在集合中
Iterator iterator() 返回迭代器
boolean remove(Object o) 删除集合中的 o
int size() 返回set中元素的个数
boolean isEmpty() 检测set是否为空,空返回true,否则返回false
Object[] toArray() 将set中的元素转换为数组返回
boolean containsAll(Collection<?> c) 集合c中的元素是否在set中全部存在,是返回true,否则返回false
boolean addAll(Collection<? extends E> c) 将集合c中的元素添加到set中,可以达到去重的效果

注意:

  • Set是继承自Collection的一个接口类
  • 只存储key,并且要求唯一
  • TreeSet底层用Map来实现,其使用key与Object的一个默认对象作为键值对插入到Map中的
  • set最大功能就是对集合中元素去重
  • 实现Set的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet基础上维护了一个双链表来纪录元素的插入顺序。
  • Set中的Key不能修改,要修改先将原来的删除
  • TreeSet中不能插入null的key,HashSet可以
  • TreeSet与HashSet区别:
Set底层结构 TreeSet HashSet
底层结构 红黑树 哈希桶
插入/删除/查找时间复杂度 (O(log2N)) (O(1))
是否有序 关于Key有序 不一定有序
线程安全 不安全 不安全
插入/删除/查找区别 按照红黑树的特性来进行插入和删除 1.先计算key的哈希地址2.然后进行插入和删除
比较与覆写 key必须能够比较,否则会抛出ClassCastException异常 自定义类型需要覆写equals和hashCode方法
应用场景 需要Key有序场景下 Key是否有序不关心,需要更高的时间性能
java 复制代码
public static void TestSet(){
Set<String> set = new TreeSet<>();
        set.add("1");
        set.add("2");
        set.add("3");
        set.add("4");
        set.add("5");
        //已存在返回false
        set.add("1");
        System.out.println(set.size());
        System.out.println(set);
        //key为null空指针异常
        //set.add(null);
        set.remove("1");
        System.out.println(set);
        //通过迭代器逐个获取集合中的元素,并输出
        Iterator<String> it= set.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
        }
        System.out.println();
    }

输出结果:

五、哈希表

5.1概念

顺序结构以及平衡树中,关键码以及其存储位置之间没有对于=应关系,因此在查找一个元素时必须结果关键码多次比较。

理想搜索方法:不经过任何比较,一次直接从表中达得到要搜索元素,通过某种函数使得以元素的存储位置与它关键码之间能建立--一一映射关系,那么将很快找到这个元素

当向该结构中
插入元素 :根据关键码的哈希函数计算存储位置并存放。
搜索元素 :对关键码计算哈希值,按该位置取元素比较,关键码相等则搜索成功。

该方法即哈希(散列)方法,哈希方法中使用转换函数称为哈希(散列)函数,构造出的结构称为哈希表(HashTable)(散列表)

  • 示例:数据集合 {1, 7, 6, 4, 5, 9} ,哈希函数 hash(key) = key % capacity ( capacity = 10 ),各元素哈希地址计算为: hash(1)=1 、 hash(7)=7 、 hash(6)=6 、 hash(4)=4 、 hash(5)=5 、 hash(9)=9 ,对应哈希表存储位置如表格所示。
哈希地址 0 1 2 3 4 5 6 7 8 9
存储元素 1 4 5 6 7 9

5.2冲突-避免

一、哈希冲突的必然性

由于哈希表底层数组容量往往小于实际要存储的关键字数量,冲突的发生是必然的,我们应尽量降低冲突率。

二、5.4 冲突-避免-哈希函数设计

(1)哈希冲突的原因

哈希函数设计不够合理可能引发哈希冲突。

(2)哈希函数设计原则
  • 定义域必须包含需要存储的全部关键码,若散列表允许有( m )个地址,其值域必须在( 0 )到( m-1 )之间。
  • 计算出的地址能均匀分布在整个空间中。
  • 函数本身应该比较简单。

常见哈希函数:

哈希函数设计方法及关键信息

方法 定义/公式 优点/特点 适用场景
直接定制法 Hash(Key) = A*Key + B (线性函数) 简单、均匀 查找范围小且连续
除留余数法 Hash(key)=key%p(p 是不大于散列表地址数( m )的质数) 常用、高效 多数哈希表场景
平方取中法 对关键字平方后抽取中间几位作为哈希地址 无需知道关键字分布 关键字位数不大且分布未知
折叠法 将关键字分割成等长部分(最后部分可短),叠加后取后几位作为地址 无需知道关键字分布 关键字位数较多
随机数法 H(key)=random(key)(随机函数) 灵活性高 关键字长度不等
数学分析法 选择关键字中分布均匀的位作为散列地址 基于符号分布均匀性设计 关键字各位符号分布有规律(如手机号等)

注意:设计越精妙,产生哈希冲突可能性越小,但不能避免

常见哈希冲突处理:闭散列(线性探测、二次探测)、开散列(链地址法)、多次散列

5.5冲突---避免------负载因子调节

一、散列表载荷因子(负载因子)

定义:

α=填入表中的元素个数/散列表的长度

意义:是散列表装满程度的标志因子。

α 越大,填入元素越多,冲突可能性越大;

α 越小,冲突可能性越小。散列表平均查找长度是

α 的函数,不同冲突处理方法对应不同函数。

二、负载因子的工程限制

对于开放定址法,负载因子需严格限制在 0.7−0.8 以下。若超过 0.8,查表时 CPU 缓存不命中(cache missing)会按指数曲线上升。例如 Java 系统库中采用开放定址法的 hash 库,限制负载因子为 0.75,超过则会 resize 散列表。

三、负载因子与冲突率的关系

从 "负载因子和冲突率的关系粗略演示" 图可见,冲突率随负载因子增大而上升,且在负载因子达到一定值后,冲突率呈快速上升趋势。当冲突率过高时,可通过降低负载因子(调整哈希表数组大小)来变相降低冲突率。

相关推荐
qq_336313932 小时前
javaweb-Maven
java·maven
Demon_Hao2 小时前
Spring Boot开启虚拟线程ScopedValue上下文传递
java·spring boot·后端
野犬寒鸦2 小时前
从零起步学习并发编程 || 第三章:JMM(Java内存模型)详解及对比剖析
java·服务器·开发语言·分布式·后端·学习·spring
一勺菠萝丶2 小时前
Jenkins 构建日志出现 `[INFO]` 乱码?原因与完整解决方案(小白必看)
java·servlet·jenkins
C雨后彩虹2 小时前
CAS 在 Java 并发工具中的应用
java·多线程·并发·cas·异步·
执着2592 小时前
力扣hot100 - 144、二叉树的前序遍历
数据结构·算法·leetcode
范纹杉想快点毕业2 小时前
嵌入式系统架构之道:告别“意大利面条”,拥抱状态机与事件驱动
java·开发语言·c++·嵌入式硬件·算法·架构·mfc
近津薪荼2 小时前
递归专题(4)——两两交换链表中的节点
数据结构·c++·学习·算法·链表
2501_940315262 小时前
【无标题】2390:从字符串中移除*
java·开发语言·算法