首页 > 其他分享 >【数据结构】Map和Set

【数据结构】Map和Set

时间:2024-08-05 18:28:07浏览次数:16  
标签:Map Set cur value set key 数据结构

目录

1.前言

2.搜索树

2.1概念

2.2操作-查找

2.3操作-插入

2.4操作-删除

2.5性能分析

3.搜索

3.1概念及场景

3.2模型

3.3Map 的使用

3.3.1关于Map的说明

3.3.2关于Map.Entry的说明,>

3.3.3Map 的常用方法说明

3.3.4TreeMap的使用

3.4Set 的使用

3.4.1Set 的说明

3.4.2Set的常用方法说明

3.4.3TreeSet的使用

4.总结


1.前言

我们上次介绍了数据结构中的7中常见的排序算法,今天要给大家分享数据结构路上的一个新征程——Map和Set,让我们一起接着往下了解并学习。

2.搜索树

2.1概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树 :
  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

比如{5,3,4,1,7,8,2,6,0,9},画成二叉搜索树如下所示:

我们首先构建二叉搜索树。

public class BinarySearchTree {
    static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(int val) {
            this.val = val;
        }
    }
}

 2.2操作-插入

1. 如果 树为空树 ,即 根 == null ,直接插入。

2. 如果树不是空树,按照查找逻辑确定插入位置,插入新结点。

    //插入
    public void insert(int val) {
        TreeNode node = new TreeNode(val);
        if (root == null) {
            root = node;
            return;
        }
        TreeNode cur = root;
        TreeNode parent = null;
        while (cur != null) {
            if (cur.val < val) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                parent = cur;
                cur = cur.left;
            }else {
                return;
            }
        }
        //parent 指向的节点 就是需要插入的节点位置 父节点
        if(parent.val >val){
            parent.left =node;
        }else {
            parent.right =node;
        }
    }

2.3操作-查找

    //查询
    public TreeNode root = null;

    public TreeNode search(int k) {
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val > k) {
                cur = cur.right;
            } else if (cur.val < k) {
                cur = cur.left;
            } else {
                return cur;
            }
        }
        return null;
    }

2.4操作-删除

设待删除结点为 cur, 待删除结点的双亲结点为 parent。 1. cur.left == null 2. cur.right == null 3. cur.left != null && cur.right != null Tips:需要使用 替换法 进行删除,即在它的右子树中寻找中序下的第一个结点 ( 关键码最小 ),用它的值填补到被删除节点中,再来处理该结点的删除问题。
    //删除
    public void remove(int key) {
        TreeNode parent = null;
        TreeNode cur = root;
        while (cur != null) {
            if (cur.val < key) {
                parent = cur;
                cur = cur.right;
            } else if (cur.val > key) {
                parent = cur;
                cur = cur.left;
            } else {
                removeNode(parent, cur);
                return;
            }
        }
    }

    private void removeNode(TreeNode parent, TreeNode cur) {
        if (cur.left == null) {
            if (cur == root) {
                root = cur.right;
            } else if (cur == parent.left) {
                parent.left = cur.right;
            } else {
                parent.right = cur.right;
            }
        } else if (cur.right == null) {
            if (cur == root) {
                root = cur.left;
            }
            if (cur == parent.left) {
                parent.left = cur.left;
            } else {
                parent.right = cur.left;
            }
        } else {
            TreeNode target = cur.right;
            TreeNode targetP = cur;
            while (target.left != null) {
                targetP = target;
                target = target.left;
            }
            cur.val = target.val;
            if (target == target.left) {
                targetP.left = target.right;
            } else {
                targetP.right = target.right;
            }
        }
    }

2.5性能分析

  • 插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
  • 对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
  • 但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树。

 下面举个例子如{6,4,3,5,8,7,9}

最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log₂N

最差情况下,二叉搜索树退化为单支树,其平均比较次数为: N/2

3.搜索

3.1概念及场景

Map set 是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关 。以前常见的搜索方式有:
1. 直接遍历,时间复杂度为 O(N) ,元素如果比较多效率会非常慢; 2. 二分查找,时间复杂度为 O(log₂N), , 但搜索前必须要求序列是有序的。

上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中的查找比如:

1. 根据姓名查询考试成绩

2. 通讯录,即根据姓名查询联系方式

3. 不重复集合,即需要先搜索关键字是否已经在集合中

可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了,本次介绍的Map和Set是一种适合动态查找的集合容器。

3.2模型

一般把搜索的数据称为关键字( Key ,和关键字对应的称为值( Value ,将其称之为 Key-value 的键值对,所以模型会有两种: 1. key 模型 ,比如:
  • 有一个英文词典,快速查找一个单词是否在词典中
  • 快速查找某个名字在不在通讯录中
2. Key-Value 模型 ,比如:
  • 统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:<单词,单词出现的次数>
  • 梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号

 而Map中存储的就是key-value的键值对,Set中只存储了Key

3.3Map 的使用

 具体可以参考[Map的官方文档]:

3.3.1关于Map的说明

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

3.3.2关于Map.Entry<K, V>的说明

Map.Entry<K, V> Map内部实现的用来存放键值对映射关系的内部类,该内部类中主要提供了<key, value> 的获取,value的设置以及Key的比较方式。

方法 解释
K getKey()返回 entry 中的 key
V getValue()返回 entry 中的 value
V setValue(V value)将键值对中的value替换为指定value

注意:Map.Entry<K,V>并没有提供设置Key的方法。

3.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<K> keySet()返回所有 key 的不重复集合
Collection values()返回所有 value 的可重复集合
Set<Map.Entry<K, V>> entrySet()返回所有的 key-value 映射关系
boolean containsKey(Object key)判断是否包含 key
boolean containsValue(Object value)判断是否包含 value

注意: 

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

3.3.4TreeMap的使用

import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class Test {
    public static void main(String[] args) {
        Map<String, Integer> map = new TreeMap<>();
        //put(K key, V value)  设置 key 对应的 value
        map.put("小米手环9", 249);
        map.put("红米K60", 3299);

        get(Object key) 返回 key 对应的 value
        System.out.println("小米手环9:" + map.get("小米手环9"));
        System.out.println("红米K60:" + map.get("红米K60"));

        //getOrDefault(Object key, V defaultValue) 返回 key 对应的 value,key 不存在,返回默认值
        System.out.println(map.getOrDefault("小米", 9999));

        //Set<K> keySet()  返回所有 key 的不重复集合
        //把所有key放在了集合Set当中
        Set<String> set = map.keySet();
        System.out.println(set);

        //containsKey(Object key) 判断是否包含 key
        System.out.println(map.containsKey("红米K60"));
        //containsValue(Object value)  判断是否包含 value
        System.out.println(map.containsValue(249));

        //remove(Object key) 删除 key 对应的映射关系
        System.out.println(map.remove("小米手环9"));//删除小米手环9

        //Set<Map.Entry<K, V>> entrySet() 返回所有的 key-value 映射关系
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            System.out.println("key:" + entry.getKey() + " value:" + entry.getValue());
        }
    }
}

3.4Set 的使用

3.4.1Set 的说明

Set 与 Map 主要的不同有两点: Set 是继承自 Collection 的接口类, Set 中只存储了 Key。具体可以参考 [Set的官方文档]:

3.4.2Set的常用方法说明

方法解释
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中,可以达到去重的效果
注意:

 1. Set是继承自Collection的一个接口类

2. Set中只存储了key,并且要求key一定要唯一

3. TreeSet的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的

4. Set最大的功能就是对集合中的元素进行去重

5. 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序。

6. Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入

7. TreeSet中不能插入null的key,HashSet可以。

8. TreeSet和HashSet的区别

Set底层结构 TreeSet HashSet
底层结构 红黑树哈希桶
插入/删除/查找时间复杂度 O(log₂N)O(1)
是否有序 关于Key有序不一定有序
线程安全 不安全不安全
插入/删除/查找区别按照红黑树的特性来进行插入和删除1. 先计算key哈希地址 2. 然后进行 插入和删除
比较与覆写key必须能够比较,否则会抛出 ClassCastException异常自定义类型需要覆写equals和 hashCode方法
应用场景 需要Key有序场景下Key是否有序不关心,需要更高的 时间性能

3.4.3TreeSet的使用

import java.util.*;

public class Test {
    public static void main(String[] args) {
        Set<String> set =new TreeSet<>();
        //add(E e)  添加元素,但重复元素不会被添加成功
        set.add("张三");
        set.add("李四");
        set.add("王五");
        System.out.println(set);
        System.out.println(set.add("张三"));

        //contains(Object o)  判断 o 是否在集合中
        System.out.println(set.contains("李四"));

        //size()  返回set中元素的个数
        System.out.println(set.size());

        //remove(Object o)  删除集合中的 o
        System.out.println(set.remove("王五"));

        //iterator()  返回迭代器
        Iterator<String> it = set.iterator();
        while(it.hasNext()){
            System.out.print(it.next() + " ");
        }
        System.out.println();

        //addAll(Collection<? extends E> c)  将集合c中的元素添加到set中,可以达到去重的效果
        String[] str ={"李四","王五","赵六"};
        set.addAll(Arrays.asList(str));
        System.out.println(set);

        //containsAll(Collection<?> c)  集合c中的元素是否在set中全部存在,是返回true,否则返回 false
        System.out.println(set.containsAll(Arrays.asList(str)));
    }
}

4.总结

本次介绍了数据结构中关于Map和Set的概念、使用以及搜索树的几种操作,熟悉并掌握以上用法,为我们编写程序时提供更加方便的使用和操作。

标签:Map,Set,cur,value,set,key,数据结构
From: https://blog.csdn.net/m0_74336101/article/details/140879965

相关文章

  • WPF WriteableBitmap通过GDI+绘制帮助类
    代码:publicclassWriteableBitmapGraphic:IDisposable{publicWriteableBitmapSource{get;privateset;}publicSystem.Drawing.Bitmapbitmap{get;privateset;}publicintDataLength{get;privateset;}publ......
  • 《数据结构习题解析与实验指导_李冬梅,张琪编著》总结出的大纲
        下面大纲为《数据结构习题解析与实验指导_李冬梅,张琪编著》总结出的大纲,可装13学习下:          ......
  • 出现 No mapping for DELETE/GET等
    出现NomappingforDELETE/GET等错误一:请求url不对修改前如下图可知后端请求url为http://localhost:8080/user/addressBook运行后控制台出现发现后端请求url比前端请求url少了/改正:在@DeleteMapping后面加上/ @DeleteMapping("/")@ApiOperation("根据id......
  • python 元类:在调用“__set_name__”方法后编辑命名空间?
    假设我们用元类定义一个类。在类主体中,分配了对象,这些对象实现__set_name__以在类的数据结构中注册自身。是否可以在运行方法之后编辑命名空间?比如,分离填充的数据结构,将其分成两部分,然后在新属性下添加部分?__set_name__问题在于,在元类中调用之......
  • 数据结构-2.单向链表的实现
    节点结构体设计structLinkNode{ //数据域 void*data; //指针域 structLinkNode*next;};data:一个void*类型的指针,指向节点存储的数据。使用void*是为了链表能够存储不同类型的数据。next:一个指向下一个LinkNode结构体的指针,形成链表的链接。链表结构体设......
  • 模拟实现 memset --浅谈C语言
    memset()描述C库函数void*memset(void*str,intc,size_tn)用于将一段内存区域设置为指定的值。memset()函数将指定的值c复制到str所指向的内存区域的前n个字节中,这可以用于将内存块清零或设置为特定值。在一些情况下,需要快速初始化大块内存为零或者特定值,memse......
  • 【转载】MapStruct使用填坑
    使用MapStruct的时候明明sourcefield不是null,转换完之后就变成null了,结果发现MapStruct生成的Converter是很久以前的,idea里面直接点运行并不会重新生成MapStruct的实现类,所以修改实体类之后一定要mvnclean。和这位仁兄碰到了一样的问题,心有戚戚焉,所以转载mapstruct是一个编译......
  • [ARC118C] Coprime Set 题解
    题目传送门(洛谷)题目传送门(atcoder)Step1理解题意输入一个数nnn要求你构造一个长度为n......
  • Android R Settings关于屏保/PowerManagerService欺骗系统不让其进入休眠状态
    //屏保设置界面./packages/apps/Settings/src/com/android/settings/dream/DreamSettings.java//和PowerManagerService建立联系./frameworks/base/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java//系统时钟屏保继承了DreamService./package......
  • 数据结构内核链表的代码
    说明内核链表的诞生原因呢,就是为了把数据域和指针域分开来就形成了下面这样的链表structlist{structlist*prev;//存放前缀节点的指针structlist*next;//存放后缀节点的指针};那有的读者会疑问,那数据放哪里?//节点结构体structnode{//以整型数......