Set和Map及哈希表介绍

news2024/11/24 4:04:13

    • 搜索方式介绍
    • TreeMap
      • Map使用
    • TreeSet
      • Set使用
    • Set和Map常用方法练习(后面补充)
    • 练习之Set/Map
    • oj练习(后面补充)
    • 哈希表
      • 哈希冲突
        • 避免冲突-哈希函数设计
        • 避免冲突-负载因子调节
        • 避免冲突-闭散列
        • 避免冲突-开散列
    • 模拟实现哈希表
    • 哈希Map源码分析

搜索方式介绍

哈希集合(Hash Set)是一种数据结构,集合(Set)的一种实现方式。哈希集合使用哈希表(Hash Table)来实现这一特性。Set和Map底下有四种实现:Map和Set适合动态查找的集合容器;TreeSet和TreeMap背后都是一棵搜索树(红黑树)

原始的搜索方式:效率低;不适对区间经常进行插入和删除操作的对象查找
1.直接遍历,时间复杂度为O(N),元素如果比较多效率会非常慢
2.二分查找,时间复杂度为,但搜索前必须要求序列是有序的
在这里插入图片描述
为什么Set和Map适合动态查找呢?这就要取决于它们的模型(Key-value的键值对)
1:纯key模型;Set只存key
比如:我要查通讯录的某个名字在不在

2:key-Value模型;Map存key-Value键值对
比如:统计单词出现的个数;key:value

TreeMap

TreeMap底层是一个搜索树(红黑树);存放的键一定是按照有序的顺序存储的,因为二叉搜索树也是有序的。
TreeMap和TreeSet的key都是要可比较的;堆的也是需要可比较的;但是堆的第一次offer是不进行比较的;所以它第一次放进去不报错;但是这里的TreeMap和TreeSet如果是不能比较的就必报错。

Map使用

Map是一个接口类,该类没有继承自Collection,该类中存储的是<K,V>结构的键值:。Map没有实现Iterable所以Map不能使用迭代器去遍历;我们后面遍历得使用特殊手段
在这里插入图片描述
1:Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap
2:Map中存放键值对的Key是唯一的,value是可以重复的
3:Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复)。
4:Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)。
5:Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行重新插入。

Map.Entry<K, V>是什么?
Map.Entry<K, V>是Map内部实现的用来存放<key, value>键值对映射关系的内部类,该内部类中主要提供了<key, value>的获取,value的设置以及Key的比较方式。(注意:这里并没有提供设置Key的方法)
在这里插入图片描述

TreeSet

底层也是一棵搜索树,存储数据的特点为有序的,不可以重复的,所有存放的元素都是可以比较,不可重复的。
TreeSet底层使用TreeMap的实现(HashSet底层使用HashMap)。TreeSet和TreeMap存元素时的key一定得可比较;不然会出现ClassCastException的异常
在这里插入图片描述
Map的遍历
1:
把Key放入set里;然后遍历获取全部value

public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<>();
        map.put("aaa",1);
        map.put("bbb",2);
        map.put("ccc",3);
        Set<String> set = map.keySet();
        for (String s : set) {
            System.out.println(s+" = "+map.get(s));
        }
    }

2:
使用Enrty然后获取里面的Key

public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<>();
        map.put("aaa",1);
        map.put("bbb",2);
        map.put("ccc",3);
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            System.out.println(entry.getKey()+" = "+entry.getValue());
        }
    }

Set使用

Set与Map主要的不同有两点:Set是继承自Collection的接口类,Set中只存储了Key;重复的key后面会覆盖前面
在这里插入图片描述

1.Set最大的功能就是对集合中的元素进行去重
2.实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序。
3.Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入
4.Set中不能插入null的key。队列是可以插入null

Set的三种遍历方法:
1:迭代器

public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        set.add(1);
        set.add(2);
        set.add(3);
        Iterator<Integer> it = set.iterator();
        while(it.hasNext()) {
            System.out.print(it.next()+" ");
        }
    }

2:for each循环

public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        set.add(1);
        set.add(2);
        set.add(3);
        for (Integer integer : set) {
            System.out.print(integer+" ");
        }
    }

Set和Map常用方法练习(后面补充)

总结:
在这里插入图片描述

练习之Set/Map

10万个为什么:如何生成10W个随机数

1:统计10W个数据中;不重复的数据 [去重]
Treeset和HashSet都能做到

    //十万个为什么之Set、Map运用。生成范围1-50000
    public static void main(String[] args) {
        int []array=new int[10_0000];
        Random random=new Random();
        for (int i = 0; i <10_0000 ; i++) {
            array[i]=random.nextInt(5_0000);
        }
        func1(array);

    }
    //先生成10w个为什么
    public static void func1(int []array){
        HashSet<Integer> set=new HashSet<>();
//        Set<Integer> set=new TreeSet<>();
        set.add(9);
        set.add(1);
        for (int i = 0; i < array.length; i++) {
            set.add(array[i]);

        }
        System.out.println(set);
    }

注意:Integer类型;没超过这int类型表示的最大数字范围;它的哈希值计算还是它本身。所以我们打印的结果就看似有序的。其实并非真正意义上的有序。

2:统计10W个数据中,第一个重复的数据库

    //统计10W个数据中,第一个重复的数据库
    public static void first(int []array){
        HashSet<Integer> set=new HashSet<>();
//        Set<Integer> set=new TreeSet<>();
        for (int i = 0; i < array.length; i++) {
            if(!set.contains(array[i])) {
                set.add(array[i]);
            }else {
                System.out.println(array[i]);
                return;

            }

        }

    }

3:统计10W个数据中每个数据出现的次数
最终结果是一个key value。key是值不重复;value是出现的次数。直接存Map里

   //统计10W个数据中每个数据出现的次数
    public static void count(int[] array){
        HashMap<Integer,Integer> map=new HashMap<>();
        for (int i = 0; i <array.length ; i++) {
            int key=array[i];
            //我获取一下看看有没有这个元素;如果没有;我得放进去(key,1)
            if(map.get(key)==null){
                map.put(key,1);
            }else {
             //里面已经有了;我们直接给value加1。那我们得先获取到value
                int val=map.get(key);
                map.put(key,val+1);

            }
            for (Map.Entry<Integer,Integer> entry:map.entrySet() ) {
                System.out.println(entry.getKey()+"出现"+entry.getValue()+"次");

            }
        }


    }

oj练习(后面补充)

在这里插入图片描述

哈希表

理想的搜索方法;不经过任何比较,一次直接从表中得到要搜索的元素。如果构造一种存储结构,通过某种函数hashFunc。使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。(数组遍历是O(N);二分查找是O(log N);搜索树是O(log N);而哈希表能达到O(1))
插入元素:根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
搜索元素:对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功

哈希表是一种数据结构:上述方式为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash Table)
例如:数据集合{1,7,6,4,5,9};
哈希函数设置为:hash(key) = key % capacity;capacity为存储元素底层空间总的大小。
在这里插入图片描述

哈希冲突

哈希冲突:不同关键字通过相同哈希函数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。
这些具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。
例如:上面的例子2和12的位置相同(2%10=2;12%10=2)

避免冲突-哈希函数设计

当我们试图将大量的数据映射到有限数量的哈希桶时;冲突是必然的;冲突没法解决;只能尽量降低冲突率
降低冲突:哈希函数设计要合理设计;需要以下设计原则
1:哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间
2:哈希函数计算出来的地址能均匀分布在整个空间中
3:哈希函数应该比较简单
常见的哈希函数:
1.直接定制法–(常用)
取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B优点:简单、均匀
缺点:需要事先知道关键字的分布情况
使用场景:适合查找比较小且连续的情况面试题:字符串中第一个只出现一次字符
2.除留余数法–(常用)
设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址

避免冲突-负载因子调节

在这里插入图片描述
在这里插入图片描述
现在知道负载因子的重要性:如何调节负载因子;存的数越来越多,前面的填入表的个数是无法改变的(你不能说不给人家存)只能改变后面的表的长度

避免冲突-闭散列

闭散列也叫开放定址法的基本思想是:当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个”空位置中去。那如何寻找下一个空位置呢?
例如:上面举的例子;如果现在需要插入元素44,先通过哈希函数计算哈希地址,下标为4,因此44理论上应该插在该位置,但是该位置已经放了值为4的元素,即发生哈希冲突。我们怎么找下一个空位置?
在这里插入图片描述
1.线性探测法;
从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。找到8的位置为空;插入进去。
当我们使用闭散列处理哈希冲突时;不能随便删掉哈希表的已有元素;哪怕不用了也不能随便删除;因为直接删除掉,44查找起来可能会受影响。所以线性探测采用标记的伪删除法来删除一个元素。

2.二次探测法;
线性探测的缺陷是产生冲突的数据堆积在一块,这与其找下一个空位置有关系,因为找空位置的方式就是挨着往后逐个去找,因此二次探测为了避免该问题,找下一个空位置的方法为:Hi=(H0+i^2)%m或者Hi=(H0- i ^2)%m;i为1,2,3……(i是第一次冲突,或者第二次冲突等等)
H0是通过散列函数Hash(x)对元素的关键码key进行计算得到的位置;m是表的大小。对于刚才要插入44,产生冲突,使用解决后的情况为:(4+1^2)%10

表的长度为质数(质数能比较好降低冲突;比如公倍数)且表负载因子a不超过0.5时,新的表项才一定能够插入,而且任何一个位置都不会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装满的情况,但在插入时必须确保表的负载因子a不超过0.5,如果超出必须考虑增容。
闭散列缺点:为了高效;空间利用率比较低,这也是哈希的缺陷。

避免冲突-开散列

开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
在这里插入图片描述
总结:采用开放地址法处理哈希冲突的时候,其平均查找长度应当大于链地址法
冲突的地方;这里是一个链表;全部堆积在这里;jdk1.7之前采用头插法;jdk1.8使用尾插法。在大集合中的搜索问题转化为在小集合中做搜索。
这里使用单链表:优点是它相对简单且占用的额外内存较少。每个节点只需存储键、值和下一个节点的引用即可。然而,单链表的缺点是在查找特定键值对时需要遍历整个链表,这可能会导致查找操作的时间复杂度为 O(n),其中 n 是链表的长度。在极端情况下,如果哈希函数选择不当,链表可能会变得非常长,影响性能。

哈希表的冲突率是不高的,冲突个数是可控的,也就是每个桶中的链表的长度是一个常数,所以,通常意义下,我们认为哈希表的插入/删除/查找时间复杂度是O(1)

模拟实现哈希表

针对int类型

import java.util.Arrays;

//int类型
public class HashBuck {

    static class Node {
        public int key;
        public int val;
        public Node next;

        public Node(int key, int val) {
            this.key = key;
            this.val = val;
        }
    }

    public Node[] array;
    public int usedSize;

    public HashBuck() {
        array = new Node[8];
    }
//我们使用头插比较简单
    public void put(int key,int val) {
        int index = key % array.length;
        //遍历Index下标的数组,如果有相同的key那么替换
        Node cur = array[index];
        while (cur != null) {
            if(cur.key == key) {
                cur.val = val;
                return;
            }
            cur = cur.next;
        }
        //进行头插法
        Node node = new Node(key, val);
        node.next = array[index];
        array[index] = node;
        usedSize++;
        if( loadFactor() >= 0.75f) {
            //扩容
            //array = Arrays.copyOf(array,2*array.length);
            resize();
        }
    }

    private void resize() {//哈希函数值变了;所以我们不能简简单单的扩容;需要根据哈希函数重新把原来的元素放到相应的位置;然后才能找得到
        Node[] newArray = new Node[2*array.length];
        for (int i = 0; i < array.length; i++) {
            Node cur = array[i];
            while (cur != null) {
                Node curNext = cur.next;
                int newIndex = cur.key % newArray.length;
                //拿着cur节点 进行插入到新的位置
                cur.next = newArray[newIndex];
                newArray[newIndex] = cur;
                cur =  curNext;
            }
        }
        array = newArray;
    }

    private float loadFactor() {
        return usedSize*1.0f / array.length;
    }

    public int get(int key) {
        int index = key % array.length;
        Node head = array[index];
        while (head != null) {
            if(head.key == key) {
                return head.val;
            }
            head = head.next;
        }
        return -1;
    }
}

针对泛型:


//泛型
//泛型需要注意;使用的是哈希code; int index = hash % array.length;。不是我们上一个代码的int类型直接key%array.length;
//
public class HashBuck2<K,V> {

    static class Node<K,V> {
        public K key;
        public V val;
        public Node<K,V> next;

        public Node(K key, V val) {
            this.key = key;
            this.val = val;
        }
    }

    public Node<K,V>[] array = (Node<K,V>[])new Node[10];
    public int usedSize;

    public void put(K key,V val) {
        int hash = key.hashCode();
        int index = hash % array.length;
        Node<K,V> cur = array[index];
        while (cur != null) {
            if(cur.key.equals(key)) {
                cur.val = val;
                return;
            }
            cur = cur.next;
        }
        Node<K,V> node = new Node<>(key, val);
        node.next = array[index];
        array[index] = node;
        usedSize++;
    }

    public V get(K key) {//先找桶再找值
        int hash = key.hashCode();
        int index = hash % array.length;
        Node<K,V> cur = array[index];
        while (cur != null) {
            if(cur.key.equals(key)) {//如果两个id相同的对象;存的key是不同;但是他们存在同一个桶里;通过equals比较取哪个
                return cur.val;
            }
            cur = cur.next;
        }
        return null;
    }

}

在这里插入图片描述

假设我要储存person这个对象:我得重写哈希code;这样子id值相同的对象才能存到一个桶里;因为是根据哈希值来计算的。最后通过key比较我们就可以取出想要的。

import java.util.*;

class Person {
    public String id;

    public Person(String id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id='" + id + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(id, person.id);
    }

    //重写了HashCode方法,根据对象的id属性来计算哈希值
    //如果两个对象的 id 属性值相同,那么它们的哈希码也会相同,这符合哈希表中的要求。
    //这两个对象被视为在哈希表中的相同位置,即它们会被放置在哈希表的同一个桶中。
    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}

hashcode和equals区别:
hashCode 是一个方法,用于计算对象的哈希码,通常是一个整数。
hashCode 方法的主要作用是提供一种快速定位对象的机制,以加速数据结构的存储和检索操作。
哈希码的计算通常基于对象的内容(属性值),但不是唯一标识对象的内容,因此可能存在哈希冲突(不同对象具有相同哈希码)。

equals 是一个方法,用于比较两个对象的内容(属性值),以确定它们是否在语义上相等。
equals 方法的主要作用是定义对象之间的相等性比较规则,通常用于业务逻辑中的对象比较。

HashCode一定要再哈希表当中才会发挥出它的意义的;一般来说我们重写equals都要重写一下HashCode;但是我们通常在其它情况都是使用不到HashCode;所以前面的学习也就一直没有重写。
在哈希表设计里:
hashcode相同equals一定相同吗
不一定;但哈希码并不完全唯一。不同的对象可以具有相同的哈希码,这种情况称为哈希冲突。而我们使用equals区分这个相同的哈希码是不是同一个对象。

equals相同hashcode一定相同吗
一定;equals一样,代表是两个对象一样,那么它的对应的HashCode的值也一定是相同的

哈希Map源码分析

哈希映射(HashMap)的底层通常使用数组和链表(或红黑树)来实现。在 Java 8 及以后的版本中,如果同一个桶中的键值对数量达到一定阈值(通常是8个),则链表会被转换为红黑树,以提高检索性能。

在这里插入图片描述
构造方法:
在这里插入图片描述

在这里插入图片描述
无参构造:
在这里插入图片描述
默认容量是0;但是我们去put却能成功。在putVal(树化的代码也在在里面)完成的(大串代码,调用无参构造方法的时候,第一次pu’t才会开辟内存)
在这里插入图片描述

分配的内存是按2的次幂分配:例如你new的是19;实际分配的是32;往大的分;往小16放不下19个
在这里插入图片描述

哈希Map如何解决哈希冲突:链地址法通常用于大多数哈希映射实现;可能还会使用其它的方法应对不同的情况

java中的:哈希map和哈希table有点区别;HashMap的key和value可以为null;为null时给你赋值为0。hashtable的key、value不能为null

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/994137.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql的索引分类

索引分类 在 MySQL 数据库&#xff0c;将索引的具体类型主要分为以下几类&#xff1a;主键索引、唯一索引、常规索引、全文索引。 分类 含义 特点 关键字 主键 索引 针对于表中主键创建的索引 默认自动创建 , 只能 有一个 PRIMARY 唯一 索引 避免同一个表中某数据列中…

C语言“牵手”速卖通商品详情数据方法,速卖通商品详情API接口,速卖通API申请指南

速卖通是全球最大的自营式电商企业之一&#xff0c;在线销售计算机、手机及其它数码产品、家电、汽车配件、服装与鞋类、奢侈品、家居与家庭用品、化妆品与其它个人护理用品、食品与营养品、书籍与其它媒体产品、母婴用品与玩具、体育与健身器材以及虚拟商品等。 速卖通平台的…

nginx日志、nginx访问控制、nginx优化

总结 nginx监控 内存&#xff0c;网络&#xff0c;磁盘&#xff0c;cpu 对nginx监控可以监控什么&#xff1f; 1、监控nginx服务存活状况&#xff08;ss -antpl 、systemctl status nginx 、pa aux | grep nginx&#xff09; 2、对nginx的运行状态进行监控 3、 监控nginx的监…

进程的同步与互斥

相关概念 临界资源与临界区 临界资源&#xff1a;同一时刻只能由一个进程使用的资源。 如打印机、磁带机、绘图仪等物理设备&#xff1b;由不同进程共享的消息队列、变量、数据、文件等软件资源 临界区&#xff1a;程序中访问临界资源的那一部分代码 进入区、退出区、剩余区&a…

六、MySql表的增删改查

CRUD : Create(创建), Retrieve(读取)&#xff0c;Update(更新)&#xff0c;Delete&#xff08;删除&#xff09; 文章目录 一、Create&#xff08;一&#xff09;语法&#xff08;二&#xff09;案例&#xff08;三&#xff09;插入情况1.单行数据 全列插入2.多行数据 指定…

Matlab之数组字符串函数汇总

一、前言 在MATLAB中&#xff0c;数组字符串是指由字符组成的一维数组。字符串可以包含字母、数字、标点符号和空格等字符。MATLAB提供了一些函数和操作符来创建、访问和操作字符串数组。 二、字符串数组具体怎么使用&#xff1f; 1、使用单引号或双引号括起来的字符序列 例…

日常开发小汇总(5)数组克隆、伪数组转换为真数组、随机排序

slice 切割数组实现克隆是浅拷贝 let arr [1,2, {name:1}] let newarr arr.slice(0); console.log(newarr) //[1,2, {name:1}] newarr[2].name 666; console.log(arr[2].name);//666 JSON实现克隆 深拷贝 let arr [1,2, {name:1}] let newarr JSON.parse( JSON.stringi…

【JS面试题】如何通过闭包漏洞在外部修改函数中的变量

✍️ 作者简介: 前端新手学习中。 &#x1f482; 作者主页: 作者主页查看更多前端教学 &#x1f393; 专栏分享&#xff1a;css重难点教学 Node.js教学 从头开始学习 ajax学习 前端面试题 文章目录 什么是闭包例 如何在函数外部修改闭包中变量 什么是闭包 闭包这个东西对新…

linux--进程通信--管道通信

IPC是各种进程间通信方式的统称。 进程间通信&#xff1a;是指在不同进程之间传播或交换信息。 IPC的方式通常有&#xff1a; 单机&#xff1a;管道&#xff08;包括无名管道和命名管道&#xff09;、消息队列、信号量、共享存储、 多机&#xff1a;Socket、Streams等 1、管道…

【计算机组成原理】十个问题带你走进计算机组成的世界

十个问题带你走进计算机组成的世界 你知道 a 1 2 这条代码是怎么被 CPU 执行的吗&#xff1f; 在计算机中&#xff0c;数据和指令是分开区域存放的&#xff0c;存放指令的区域的地方称为正文段&#xff0c;存放数据的区域称为数据段。 例如下图中&#xff0c;数据1和数据2…

Redis配置

关系型数据库和非关系型数据库 ①了解关系和非关系 关系型数据库 一个结构化的数据库&#xff0c;创建在关系模型基础上&#xff0c;一般面向于记录&#xff0c;包括Oracle、MySQL、SQL Server、Microsoft Access、DB2、postgreSQL等 非关系型数据库 除了主流的关系型数据库…

java 歌词解析 源代码, 在windows10下调试运行成功。

需要两个素材。 歌词与音乐.wav package week3.exam6;public class Info {private final String info;public Info(String info){this.infoinfo;}public String getInfo() {return info;}public String toString(){return info;} }package week3.exam6;public class Lyric ext…

华为云新用户云服务器优惠价格表

华为云服务器作为业界领先的云服务提供商之一&#xff0c;一直致力于为全球用户提供高效、稳定、安全的云服务。为了帮助新用户更好地了解华为云服务器的价格和优惠活动&#xff0c;本文将详细介绍华为云服务器对新用户的优惠价格表。 一、华为云耀云服务器L实例价格表 华为云…

JavaScript基础知识09——数据类型

哈喽&#xff0c;大家好啊&#xff0c;这里是雷工笔记&#xff0c;我是雷工。 数据类型比较常见&#xff0c;无论是对程序员&#xff0c;还是电气工程师来说&#xff0c;都再熟悉不过了&#xff0c;这里跟着教程了解一下&#xff0c;主要看跟自己以往在其他PLC&#xff0c;C#&a…

2023-09-09 LeetCode每日一题(课程表)

2023-09-09每日一题 一、题目编号 207. 课程表二、题目链接 点击跳转到题目位置 三、题目描述 你这个学期必须选修 numCourses 门课程&#xff0c;记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出&#xff0c;其中…

【牛客面试必刷TOP101】Day2.判断链表中是否有环和链表中倒数最后k个结点

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01;&#xff…

python-jieba库

jieba库&#xff0c;python提供的中文分词函数库的第三方库&#xff0c;它可以将一段中文文本分割成中文词语序列。 安装jieba库 pip install jiebajieba的三个模式 全模式 - - - jieba.lcut(s,cut_allTrue) - - - 速度非常快&#xff0c;但有冗余数据 精确模式&#xff08;…

窗口延时、侧输出流数据处理

一 、 AllowedLateness API 延时关闭窗口 AllowedLateness 方法需要基于 WindowedStream 调用。AllowedLateness 需要设置一个延时时间&#xff0c;注意这个时间决定了窗口真正关闭的时间&#xff0c;而且是加上WaterMark的时间&#xff0c;例如 WaterMark的延时时间为2s&…

嵌入式开发-绪论

目录 一.什么是嵌入式 1.1硬件系统 1.2软件系统 二.嵌入式应用场景 2.1消费电子 2.1.1智能家居 2.1.2影音 2.1.3家用电器 2.1.4玩具游戏机 2.2通信领域 2.2.1对讲机 2.2.2手机 2.2.3卫星 2.2.4雷达 2.3控制领域 2.3.1机器人 2.3.2采集器PLC 2.4金融 2.4.1POS…

快速文件复制与删除工具,将复制时文件夹里的原文件删除掉

无论是工作还是生活&#xff0c;我们都离不开文件的复制和管理。然而&#xff0c;手动复制文件不仅费时费力&#xff0c;而且容易出错。现在&#xff0c;我们为您推荐一款快速文件复制与删除工具&#xff0c;让您的文件管理更加高效&#xff01; 首先&#xff0c;我们要进入文…