数据结构《MapSet哈希表》

文章目录

  • 一、搜索树
    • 1.1 定义
    • 1.2 模拟实现搜索
  • 二、Map
    • 2.1 定义
    • 2.2 Map.Entry
    • 2.3 TreeMap的使用
    • 2.4 Map的常用方法
  • 三、Set
    • 3.1 定义
    • 3.2 TreeSet的使用
    • 3.3 Set的常用方法
  • 四、哈希表
    • 4.1 哈希表的概念
    • 4.2 冲突
      • 4.2.1 冲突的概念
      • 4.2.2 冲突的避免
        • 1. 选择合适的哈希函数
        • 2. 负载因子调节
      • 4.2.3冲突的解决
        • 1. 闭散列
        • 2. 开散列
    • 4.3 模拟实现
  • 五、例题练习
  • 总结


一、搜索树

1.1 定义

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

1.2 模拟实现搜索

public class MyBinarySearchTree {
    static class TreeNode{
        int key;
        TreeNode left;
        TreeNode right;
        public TreeNode(int key){
            this.key = key;
        }
    }
    private TreeNode root = null;


    /**
     * 查询节点
     * 查询是否有节点的key值为所需要找的,如果有返回这个节点,没有则返回null
     * @param key
     * @return TreeNode
     */
    public TreeNode search(int key){
        if(root == null){
            return null;//如果树为空,不用查询直接返回null
        }
        TreeNode cur = root;
        while (cur != null){
            if(key == cur.key){
                return cur;
            }else if(key < cur.key){//如果查询的值比节点的值小,说明只能在左子树上找
                cur = cur.left;
            }else {
                cur = cur.right;
            }
        }

        return null;
    }


    /**
     * 插入数据
     * 如果树为空直接插入,如果比节点的值大,往右边走,小,往左走,相等则不插入
     * @param key
     * @return
     */
    public boolean insert(int key){
        if(root == null){
            root = new TreeNode(key);
            return true;//树为空,直接插入并返回正确
        }
        TreeNode cur = root;
        TreeNode parent = null;//parent的存在,是为了让我们可以返回到上一个节点
        while (cur != null){
            if(key == cur.key){//如果要插入的数据已经存在,说明不需要插入
                return false;
            }else if(key < cur.key){//如果要插入的数据比节点值小,说明要往节点的左子树中插入
                parent = cur;
                cur = cur.left;
            }else {//如果要插入的数据比节点值大,说明要往节点的右子树中插入
                parent = cur;
                cur = cur.right;
            }
        }
        //到这里,说明我们cur已经为空,此时要插入的数据在parent的左子树或者右子树
        TreeNode treeNode = new TreeNode(key);
        if(key < parent.key){
            parent.left = treeNode;
        }else {
            parent.right = treeNode;
        }
        return true;
    }

    /**
     * 删除节点
     * 首先我们要找到这个删除的节点
     * @param key
     * @return
     */
    public boolean remove(int key){
        //首先要先找到这个点
        if(root == null){
            return false;//树为空无法删除
        }
        TreeNode parent = null;
        TreeNode cur = root;
        while (cur != null){
            if(key < cur.key){
                parent = cur;
                cur = cur.left;
            }else if(key > cur.key){
                parent = cur;
                cur = cur.right;
            }else {//说明找到这个点了
                return removeNode(parent,cur);
            }
        }

        return false;
    }

    /**
     * 删除节点的真正方法
     * @param parent
     * @param cur
     * @return
     */
    private boolean removeNode(TreeNode parent, TreeNode cur) {
        if(cur.left == null){//当要删除的节点左节点为空
            if(cur == root){//当节点为root时,那直接让root往右子树走即可
                root = cur.right;
            }else if(cur == parent.left){//节点不为root,说明cur为parent的左或者右孩子
                parent.left = cur.right;//因为cur左为空,所以父亲节点的左节点直接接上cur的右子树即可
            }else {
                parent.right = cur.right;
            }

        }else if(cur.right == null){//当要删除的节点右节点为空
            if(cur == root){//当节点为root时,那直接让root往左子树走即可
                root = cur.left;
            }else if(cur == parent.left){
                parent.left = cur.left;
            }else {
                parent.right = cur.left;
            }
        }else {//当要删除的节点左右均不为空
            //找这个节点的左子树的最右边的节点或者右子树最左边的节点来替换数值
            TreeNode targetParent = cur;
            TreeNode target = cur.left;
            while (target.right != null){
                targetParent = target;
                target = target.right;
            }
            cur.key = target.key;
            targetParent.right = target.left;
        }

        return true;
    }


}

关于上述搜索树中其他的方法,比较简单,看代码大概可以了解如何实现,这里我们主要解释一下删除中的左右孩子节点都存在的情况下删除的原理
在这里插入图片描述
在这里插入图片描述
最后删除完的结果
在这里插入图片描述


测试用例

public class Test {
    public static void main(String[] args) {
        MyBinarySearchTree myBinarySearchTree = new MyBinarySearchTree();
        myBinarySearchTree.insert(5);
        myBinarySearchTree.insert(4);
        myBinarySearchTree.insert(6);
        myBinarySearchTree.insert(9);
        myBinarySearchTree.insert(3);
        myBinarySearchTree.insert(2);
        myBinarySearchTree.insert(7);
        myBinarySearchTree.insert(8);
        myBinarySearchTree.insert(10);
        MyBinarySearchTree.TreeNode treeNode = myBinarySearchTree.search(9);
        System.out.println(treeNode.key);
        System.out.println(myBinarySearchTree.remove(9));

    }
}

在这里插入图片描述
在这里插入图片描述


什么是键值对?
键(key):它是独一无二的标识符,用于快速定位和检索对应的值。在一个Map集合里,不允许有两个相同的键,就像现实生活中每个人都有唯一的身份证号。常见的数据类型都能充当键,比如String、Integer,不过要求键对象必须正确重写hashCode()和equals()方法,以此保证唯一性和一致性。
值(value):与键关联的数据,可以是任意类型,例如在统计单词出现次数的场景中,单词是键,出现的次数作为值,这个次数值就是Integer类型。一个键对应一个值,但不同键可以对应相同的值 。

二、Map

2.1 定义

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

注意:

  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删除掉,然后再来进行重新插入。

2.2 Map.Entry

Map.Entry是一个接口,它表示Map接口中的一个键值对。每个实现了Map接口的类,例如HashMap、TreeMap等,内部都有对应的内部类实现了Map.Entry接口,用于封装单个的键值对元素,方便对Map进行遍历等操作。

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

如在HashMap中
在这里插入图片描述
在TreeMap中
在这里插入图片描述
在这里插入图片描述


2.3 TreeMap的使用

put(key, value):插入key-value的键值对
如果key不存在,会将key-value的键值对插入到map中,返回null
如果key之前是存过的,会将新的value值作为返回值返回,同时会将新的value值与之前的进行替换

Map<String,Integer> map = new TreeMap<>();
        //放入不同的人以及他的分数
        map.put("zhangsan",90);
        map.put("lisi",80);
        map.put("wangwu",88);
        Integer m = map.put("zhaoliu",92);
        Integer n = map.put("zhaoliu",99);
        System.out.println("m "+m);
        System.out.println("n "+n);

在这里插入图片描述


map.size()//返回map中已经存的键值对的个数

在这里插入图片描述


get()

		Map<String,Integer> map = new TreeMap<>();
        //放入不同的人以及他的分数
        map.put("zhangsan",90);
        map.put("lisi",80);
        map.put("wangwu",88);
        Integer m = map.put("zhaoliu",92);
        Integer n = map.put("zhaoliu",99);
		// get(key): 返回key所对应的value
        // 如果key存在,返回key所对应的value
        // 如果key不存在,返回null
        System.out.println(map.get("zhangsan"));
        System.out.println(map.get("wangmm"));

在这里插入图片描述


getOrDefault()
		//getOrDefault(): 如果key存在,返回与key所对应的value,如果key不存在,返回一个默认值
        System.out.println(map.getOrDefault("zhangsan", -100));
        System.out.println(map.getOrDefault("wangmm", -100));

在这里插入图片描述


containKey()
		//containKey(key):检测key是否包含在Map中,时间复杂度:O(logN)
        // 按照红黑树的性质来进行查找
        // 找到返回true,否则返回false
        System.out.println(map.containsKey("zhangsan"));
        System.out.println(map.containsKey("wangmm"));

在这里插入图片描述


containValue()
		// containValue(value): 检测value是否包含在Map中,时间复杂度: O(N)
        // 找到返回true,否则返回false
        System.out.println(map.containsValue(90));
        System.out.println(map.containsValue(100));

在这里插入图片描述


keySet()

返回类型: keySet()方法返回一个Set集合,因为Map中的键具有唯一性,所以用Set来存储很合适,Set集合会自动去除重复元素,保证每个键在集合里只出现一次。

遍历Map:获取到键的集合后,可以通过遍历这个Set,再配合Map的get方法,来遍历整个Map的键值对,这是一种常见的Map遍历方式。

		// 打印所有的key
		// keySet是将map中的key放入在Set中返回的
		for (String key : map.keySet()) {
            System.out.println(key + " : " + map.get(key));
        }

在这里插入图片描述


values()
		// 打印所有的value
        // values()是将map中的value放在collect的一个集合中返回的
        for(Integer integer : map.values()){
            System.out.print(integer + " ");
        }
        System.out.println();

在这里插入图片描述


entrySet()
		// 打印所有的键值对
        // entrySet(): 将Map中的键值对放在Set中返回了
        for(Map.Entry<String, Integer> entry : map.entrySet()){
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        System.out.println();

在这里插入图片描述

2.4 Map的常用方法

方法解释
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)判断是否包含valuel

三、Set

3.1 定义

Set与Map主要的不同有两点:Set是继承自Collection的接口类,Set中只存储了Key。

注意:

  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可以。

在这里插入图片描述


3.2 TreeSet的使用

add()
TreeSet<String> treeSet = new TreeSet<>();

        // add(key): 如果key不存在,则插入,返回ture
        // 如果key存在,返回false
        treeSet.add("zhangsan");
        treeSet.add("lisi");
        treeSet.add("wangwu");
        boolean m = treeSet.add("zhaoliu");
        boolean n = treeSet.add("zhangsan");
        System.out.println(m);
        System.out.println(n);

在这里插入图片描述


size()

在这里插入图片描述


contains()
		// contains(key): 如果key存在,返回true,否则返回false
        System.out.println(treeSet.contains("zhangsan"));
        System.out.println(treeSet.contains("wangmm"));

在这里插入图片描述

3.3 Set的常用方法

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

四、哈希表

4.1 哈希表的概念

哈希表是一种数据结构,它使用哈希函数将键(Key)映射到存储桶(Bucket)或槽(Slot),以便可以在平均情况下以O(1)的时间复杂度进行快速的插入、删除和查找操作。

基本原理:

  1. 哈希表由一个数组(也称为哈希表本身)和一个哈希函数组成。
  2. 哈希函数:将键作为输入,通过某种计算得到一个整数索引,这个索引就是存储键值对的存储桶的位置。例如,对于键 k,哈希函数 h(k) 会计算出一个整数 i,表示键值对 (k, v) 应该存储在数组的第 i 个位置。
    在这里插入图片描述

4.2 冲突

4.2.1 冲突的概念

当两个不同的键通过哈希函数计算得到相同的哈希值时,会发生哈希冲突。

在上面我们给出的例子中,没有出现冲突,如果通过例子中的hash函数,我们再放入一个数为44,那么我们就会和4冲突

4.2.2 冲突的避免

1. 选择合适的哈希函数

选择哈希函数的考虑因素:

  1. 分布均匀性:
    理想的哈希函数应该将键均匀地分布在哈希表的存储桶中,避免大量冲突。
  2. 计算速度:
    哈希函数的计算应该快速,避免复杂的计算,尤其是对于大量的插入、查找操作。
  3. 抗碰撞性:
    对于安全相关的应用,需要更高的抗碰撞性,使用加密哈希函数;对于普通的哈希表,可能更注重速度和分布均匀性。

常用的函数:

  1. 取模法: 对于一个整数键 k 和哈希表大小 m,哈希函数 h(k) = k % m
  2. 直接定制法:取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B
2. 负载因子调节

负载因子是哈希表中存储元素的数量与哈希表的容量(即存储桶的数量)的比值。
计算公式为:负载因子 = 元素数量 / 哈希表容量。
在这里插入图片描述

作用和意义:

  1. 它是一个重要的性能指标,用于衡量哈希表的填充程度。
  2. 在 Java 的 HashMap 中,负载因子决定了何时对哈希表进行扩容操作。
  3. 合理的负载因子可以平衡空间使用和性能,避免过多的哈希冲突。

在这里插入图片描述

常见的负载因子取值:

  1. 在 Java 的 HashMap 中,默认的负载因子是 0.75。
  2. 当元素数量达到 哈希表容量 * 负载因子 时,HashMap 会自动扩容,通常扩容为原来的两倍,并重新哈希元素。

4.2.3冲突的解决

1. 闭散列

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,可以把key存放到冲突位置中的下一个空位置中去。

  1. 线性探测:如果发生冲突,顺序地检查下一个槽,直到找到一个空槽。
    采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。比如删除元素4,如果直接删除掉,44查找起来可能会受影响。因此线性探测采用标记的伪删除法来删除一个元素
    在这里插入图片描述

  2. 二次探测(Quadratic Probing):使用二次函数来确定下一个检查的槽,比如检查h(k) + 1^2 ,h(k) + 2^2,h(k) + 3^2,等等。

闭散列性能受探测序列和负载因子影响,当负载因子较高时,性能下降明显,空间利用率低。


2. 开散列

开散列法又叫链地址法,首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
在这里插入图片描述
哈希桶其实可以看作将大集合的搜索问题转化为小集合的搜索问题,那如果冲突严重,就意味着小集合的搜索性能其实也时不佳的,这个时候我们就可以将这个所谓的小集合搜索问题继续进行转化:

  1. 每个桶的背后是另一个哈希表
  2. 每个桶的背后是一棵搜索树

4.3 模拟实现

在这里插入图片描述
这里是我自己的一些理解,可能不是那么全面,如果有不对的地方,还望指正
在这里插入图片描述
节点的插入
在这里插入图片描述
数组的扩展
在这里插入图片描述
代码实现


public class HashTable<K,V> {
    private static final double DEFAULT_LOAD_FACTOR = 0.75f;
    static class Node<K,V>{
        public K key;
        public V value;
        public Node<K,V> next;
        public Node(K key,V value){
            this.key = key;
            this.value = value;
        }
    }
    //先定义一个数组来作为哈希表存放
    private Node<K,V>[] array = (Node<K,V>[])new Node[10];

    private int useSize = 0;


    public void push(K key,V value){
        int hashcode = key.hashCode();//获得哈希值
        int index = hashcode % array.length;
        Node<K,V> cur = array[index];
        while (cur != null){
            if(key.equals(cur.key)){//如果key在链表中存在,我们就更新它的value值,不新添节点,key唯一性
                cur.value = value;
                return;
            }
            cur = cur.next;
        }
        Node<K,V> node = new Node<>(key,value);
        node.next = array[index];
        array[index] = node;
        useSize++;
        if(doLoadFactor() > DEFAULT_LOAD_FACTOR){
            resize();
        }
    }

    private void resize() {
        Node<K,V>[] newarray = (Node<K,V>[])new Node[array.length*2];
        for (int i = 0; i < array.length; i++) {
            Node<K,V> cur = array[i];
            while (cur != null){
                int newHashcode = cur.key.hashCode();
                int newIndex = newHashcode % newarray.length;//重新计算位置
                Node<K,V> curN = cur.next;//记录节点的下一个节点
                cur.next = newarray[newIndex];
                newarray[newIndex] = cur;
                cur = curN;
            }
        }
        array = newarray;
    }

    private double doLoadFactor() {
        return useSize * 1.0 / array.length;
    }

    public V getVal(K key){
        int hashcode = key.hashCode();
        int index = hashcode % array.length;
        Node<K,V> cur = array[index];
        while (cur != null){
            if(cur.key.equals(key)){
                return cur.value;
            }
            cur = cur.next;
        }
        return null;
    }
}

在这里插入图片描述
当负载因子过大时,进行扩容
在这里插入图片描述
在这里插入图片描述


五、例题练习

例题1 字符串中的第一个唯一字符
通过HashMap来实现

class Solution {
    public int firstUniqChar(String s) {
        Map<Character,Integer> map = new HashMap<Character,Integer>();
        for(int i = 0; i < s.length(); i++){
            char ch = s.charAt(i);
            map.put(ch,map.getOrDefault(ch,0)+1);
        }
        for(int i = 0; i < s.length(); i++){
            if(map.get(s.charAt(i)) == 1){
                return i;
            }
        }
        return -1;

    }
}

例题2 只出现一次的数字

使用set

遍历数组,将数组中的数字放入set中,在放入之前判断set中是否存在这个数,如果存在,将set中的这个数出出去,最后遍历set中剩下的数便是出现一次的数

class Solution {
    public int singleNumber(int[] nums) {
        HashSet<Integer> set = new HashSet<>();
        for(int i = 0; i < nums.length; i++){
            if(set.contains(nums[i])){
                set.remove(nums[i]);
            }else{
                set.add(nums[i]);
            }
        }
        for(int i = 0; i < nums.length; i++){
            if(set.contains(nums[i])){
                return nums[i];
            }
        }
        return 0;
    }
}

例题3 随机链表的复制
在这里插入图片描述

class Solution {
    public Node copyRandomList(Node head) {
        HashMap<Node,Node> map = new HashMap<>();
        Node cur = head;
        while(cur != null){
            Node node = new Node(cur.val);
            map.put(cur,node);//key为老节点,value为新节点
            cur = cur.next;
        }
        cur = head;
        while(cur != null){
            map.get(cur).next = map.get(cur.next);
            map.get(cur).random = map.get(cur.random);
            cur = cur.next;
        }
        return map.get(head);
    }
}

例题4 宝石与石头
运用set存储宝石,然后看石头中在set中能不能找到

class Solution {
    public int numJewelsInStones(String jewels, String stones) {
        HashSet<Character> set = new HashSet<>();
        int count = 0;
        for(int i = 0; i < jewels.length(); i++){
            set.add(jewels.charAt(i));
        }
        for(int i = 0; i < stones.length(); i++){
            if(set.contains(stones.charAt(i))){
                count++;
            }
        }
        return count;
    }
}

例题5 旧键盘
将实际输入的字符串放入setReal中,然后从正确的字符串中一个一个取出字符看,实际输入的字符串中有没有,没有就打印并且将它放入已经判断后的set中,以防反复判断。

import java.util.Scanner;
import java.util.HashSet;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String strAct = in.nextLine();//正确的字符串
        String strBroken = in.nextLine();//实际输入的字符串
        strAct = strAct.toUpperCase();
        strBroken = strBroken.toUpperCase();
        HashSet<Character> hashSetReal = new HashSet<>();//实际输入的
        HashSet<Character> hashSetAlready = new HashSet<>();//已经判断过的
        for(int i = 0; i < strBroken.length(); i++){
            hashSetReal.add(strBroken.charAt(i));
        }
        for(int i = 0; i < strAct.length(); i++){
            char ch = strAct.charAt(i);
            if(!hashSetReal.contains(ch) && !hashSetAlready.contains(ch)){
                hashSetAlready.add(ch);
                System.out.print(ch);
            }
        }


    }
}

总结

本篇文章,介绍了有关Map和Set,以及哈希表相关的数据结构内容,如果有什么不正确不严谨的地方,还望指正,我会尽快更改,谢谢大家!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥与力扣刷题(709 转换成小写字母)

题目&#xff1a;给你一个字符串 s &#xff0c;将该字符串中的大写字母转换成相同的小写字母&#xff0c;返回新的字符串。 示例 1&#xff1a; 输入&#xff1a;s "Hello" 输出&#xff1a;"hello"示例 2&#xff1a; 输入&#xff1a;s "here…

unity学习16:unity里向量的计算,一些方法等

目录 1 unity里的向量&#xff1a; 2 向量加法 2.1 向量加法的几何意义 2.2向量加法的标量算法 3 向量减法 3.1 向量减法的几何意义 3.2 向量减法的标量算法 4 向量的标量乘法 5 向量之间的乘法要注意是左乘 还是右乘 5.1 注意区别 5.2 向量&#xff0c;矩阵&#x…

算法3(力扣83)-删除链表中的重复元素

1、题目&#xff1a;给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 2、实现&#xff08; 因为已排序&#xff0c;所以元素若重复&#xff0c;必然在其下一位&#xff09;&#xff08;这里为在vscod…

深度学习基础知识

深度学习是人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;领域的一个重要分支&#xff0c;以下是对深度学习基础知识的归纳&#xff1a; 一、定义与原理 定义&#xff1a;深度学习是一种使计算机能够从经验中学习并以概念层次结构的方式理解世界的机…

基于mediapipe的手势游戏控制

基于mediapipe的手势游戏控制 ​ 玩游戏&#xff0c;那不是有手就行!!! mediapipe介绍 ​ Mediapipe是Google在2019年开发并提出的一款开源的跨平台多媒体处理框架&#xff0c;用于构建基于机器学习的应用程序&#xff0c;特别是涉及到计算机视觉、音频处理、姿势估计等领域。…

015: 深度学习之正向传播和反向传播

本文为合集收录&#xff0c;欢迎查看合集/专栏链接进行全部合集的系统学习。 合集完整版请参考这里。 上一节介绍了训练和推理的概念&#xff0c;这一节接着训练和推理的概念讲一下&#xff0c;神经网络的正向传播和反向传播。 正反向传播 其实单看正向传播和反向传播这两个…

2025.1.15——二、字符型注入

一、基本操作&#xff1a;整理已知信息&#xff0c;本题为字符型注入 二、解题步骤 step 1&#xff1a;确认为字符型注入 键入&#xff1a; 1 键入&#xff1a;1 and 12 # 发现报错 键入&#xff1a;1 and 11 # 未发现报错 所以确认为字符型注入 step 2&#xff1a;查询…

UML系列之Rational Rose笔记四:时序图(顺序图_序列图)

时序图有很多画法&#xff0c;这基本上能算rose里面要求最乱的一种图了&#xff1b;有些人的需求是BCE模式&#xff0c;这是正常规范点的&#xff0c;有些人就不需要&#xff0c;有些需要用数据库交互&#xff0c;有些不需要&#xff1b;没有一个较为统一的需求&#xff1b;在此…

LabVIEW水位监控系统

LabVIEW开发智能水位监控系统通过集成先进的传感技术与控制算法&#xff0c;为工业液体存储提供精确的水位调控&#xff0c;保证了生产过程的连续性与安全性。 项目背景 在化工和饮料生产等行业中&#xff0c;水位控制的准确性对保证生产安全和提高产品质量至关重要。传统的水…

STC的51单片机LED点灯基于KEIL

前言&#xff1a; 该文源于回答一个朋友的问题&#xff0c;代码为该朋友上传&#xff0c;略作修改&#xff0c;在此说明问题以及解决问题的思路&#xff0c;以减少新手错误。 电路图&#xff1a; 该位朋友未上传电路图&#xff0c;说明如下&#xff1a; stc8g1k08a-sop8控制…

基于YOLOv8的卫星图像中船只检测系统

基于YOLOv8的卫星图像中船只检测系统 (价格90) 使用的是 MASATI-V2 数据集 训练集 3617张 验证集 452张 测试集 453张 包含 [boat] [船只] 1个类 通过PYQT构建UI界面&#xff0c;包含图片检测&#xff0c;视频检测&#xff0c;摄像头实时检测。 &#xff08;该系…

用 Python 自动化处理日常任务

&#x1f496; 欢迎来到我的博客&#xff01; 非常高兴能在这里与您相遇。在这里&#xff0c;您不仅能获得有趣的技术分享&#xff0c;还能感受到轻松愉快的氛围。无论您是编程新手&#xff0c;还是资深开发者&#xff0c;都能在这里找到属于您的知识宝藏&#xff0c;学习和成长…

mac intel芯片下载安卓模拟器

一、调研 目前主流两个模拟器&#xff1a; 雷神模拟器 不支持macosmumu模拟器pro版 不支持macos intel芯片 搜索到mumu的Q&A中有 “Intel芯片Mac如何安装MuMu&#xff1f;” q&a&#x1f517;&#xff1a;https://mumu.163.com/mac/faq/install-on-intel-mac.html 提…

python中的RPA->playwright自动化录制脚本实战案例笔记

playwright录制功能使用绕过登录操作 1、首先安装playwright pip install playwright2、 安装支持的浏览器 playwright install # 安装支持的浏览器&#xff1a;cr, chromium, ff, firefox, wk 和 webkit3、接着在自己的项目下运行录制命令&#xff1a; playwright codegen…

【原创】大数据治理入门(2)《提升数据质量:质量评估与改进策略》入门必看 高赞实用

提升数据质量&#xff1a;质量评估与改进策略 引言&#xff1a;数据质量的概念 在大数据时代&#xff0c;数据的质量直接影响到数据分析的准确性和可靠性。数据质量是指数据在多大程度上能够满足其预定用途&#xff0c;确保数据的准确性、完整性、一致性和及时性是数据质量的…

数据结构(Java版)第八期:LinkedList与链表(三)

专栏&#xff1a;数据结构(Java版) 个人主页&#xff1a;手握风云 目录 一、链表中的经典面试题 1.1. 链表分割 1.2. 链表的回文结构 1.3. 相交链表 1.4. 环形链表 一、链表中的经典面试题 1.1. 链表分割 题目中要求不能改变原来的数据顺序&#xff0c;也就是如上图所示。…

ASP.NET Core - 配置系统之自定义配置提供程序

ASP.NET Core - 配置系统之自定义配置提供程序 4. 自定义配置提供程序IConfigurationSourceIConfigurationProvider 4. 自定义配置提供程序 在 .NET Core 配置系统中封装一个配置提供程序关键在于提供相应的 IconfigurationSource 实现和 IConfigurationProvider 接口实现&…

MPLS原理及配置

赶时间可以只看实验部分 由来&#xff1a;90年代中期&#xff0c;互联网流量的快速增长。传统IP报文依赖路由器查询路由表转发&#xff0c;但由于硬件技术存在限制导致转发性能低&#xff0c;查表转发成为了网络数据转发的瓶颈。 因此&#xff0c;旨在提高路由器转发速度的MPL…

【韩顺平Java笔记】第8章:面向对象编程(中级部分)【327-337】

327. 断点调试&#xff08;Debug&#xff09; 一个实际需求 在开发中&#xff0c;程序员在查找错误时&#xff0c;可用断点模式在断点调试过程中&#xff0c;是运行状态&#xff0c;是以对象的运行类型来执行的。 A extends B; B b new A(); b.xx();//按照运行类型来执行的 …

Qt 各版本选择

嵌入式推荐用 Qt4.8&#xff0c;打包的程序小&#xff1a;Qt4.8.7是Qt4的终结版本&#xff0c;是Qt4系列版本中最稳定最经典的 最后支持xp系统的长期支持版本&#xff1a;Qt5.6.3&#xff1b;Qt5.7.0是最后支持xp系统的非长期支持版本。 最后提供mysql数据库插件的版本&#xf…