class 032 位图

news2024/10/9 0:40:57

这篇文章是看了“左程云”老师在b站上的讲解之后写的, 自己感觉已经能理解了, 所以就将整个过程写下来了。

这个是“左程云”老师个人空间的b站的链接, 数据结构与算法讲的很好很好, 希望大家可以多多支持左程云老师, 真心推荐.

左程云的个人空间-左程云个人主页-哔哩哔哩视频 (bilibili.com)

在这里插入图片描述

1. 位图的原理

位图是一个类似于哈希表的“集合结构”,

哈希表可以放置不同的整数, 一个整数占据一个 int(32个bit位).无论是放 1 还是 10亿, 都是占据 32个bit位. 可以放置很多的数字,

位图和哈希表类似, 而且极大的节省了空间, 一个数字只占据 一个 bit位, 但是不能放置太多的数字, 而且要保证所有数字必须是一个连续的区间.

对比:在这里我们只是探讨(int)整数类型的.

  1. 数据要求:
    1. 哈希表:不用是连续的整数, 任何数字都可以, 1, 3, 20亿. 任何数字都能随机加入哈希表.
    2. 位图:必须是一段连续的整数比如:0 ~ 200万, 确定了之后不能有其余的数字加入位图. 要是我们只是加入 1 和 20亿 这两个数字, 使用位图, 这就需要创建一个很巨大的数组, 空间上反而不划算了, 所以需要一个连续的区间.
  2. 占据空间
    1. 哈希表:一个数字占据一个 int(32个bit位) 空间
    2. 位图:一个数字占据一个 bit位 空间. 极大地节省了空间.
  3. 效率高低
    1. 哈希表:各种操作的平均时间复杂度是 O(1), 但是最坏情况有可能到达 O(n).
    2. 位图:各种操作的时间复杂度是 O(1), 是确定的.

注意:位图在使用的之前, 必须要先给定范围, 比如给定的范围是:0 ~ 1000, 在初始化的时候就能确定范围了. 以后所有数字(0 ~ 1000)进来的时候就不会额外占据空间了, 加入我想要放入 1234 这个数字, 是不能放入的, 因为不在这个范围内.

1.1 原理实现

假设此时我们需要存储 0 ~ 100 之间的数字, 一个 int 类型有 32个bit位,
所以 32(int) * 4 == 128 > 100, 所以我们可以知道需要 4int 类型来存储.

int:[a][b][c][d], 此时我们创建了 1int 类型的数组, 长度为: 4 , 我们可以用来存储 1 ~ 100 之间的数字了, a(0 ~ 31), b(32 ~ 63), c(64 ~ 95), d(96 ~ 127). 这样就能进行存储了.

我们利用每一个 bit位, 要是 bit位1 说明对应的数字存在, 是 0 就代表对应的数字不存在.

从这里就能看出来, 位图极大地节省了空间, 非常方便. 我们只是用了 int[4] 就表示好了.

1.2 如何找到对应的位置

我们还是按照上面的数据:我们创建一个数字 int[4], 下标是 0, 1, 2, 3.

假设我们要找一个数字 x 对应的位置, 应该让 x / 32 这样找到的就是对应的 int 位置.
如果找找到这个数字在 int位置 中对应的 bit 位, 就直接让 x % 32.

举一个具体例子:假设我们要找 66 这个数字,
我们先找到它在哪一个下标:66 / 32 == 2, 所以在下标为 2int 位置.
然后我们找 66 在哪一个 bit位66 % 32 == 2, 所以在第二个位置 我们认为还是0, 1, 2 所以是第二个位置.(说第三个位置也是可以的, 但是我们这里就为了统一就直接说第二个位置).\

在这里插入图片描述

2. 位图的各种操作的实现

2.1 位图的各种操作

在这里插入图片描述

2.2 位图各种操作的实现

2.2.1 位图的初始化(利用构造函数)

这个意味着位图的初始化, 先给我一个数字 n, 位图需要存储的范围是:[0, n - 1]. 举几个例子说明:

  1. 假设 n == 1, 需要存储的范围是:[0, 0] 那我需要有一个 int(32位) 类型的空间. 这是最低标准
  2. 假设 n == 2 , 需要存储的范围是:[0, 1] 那我也需要有一个 int(32位) 类型的空间.
  3. 假设 n == 32 , 需要存储的范围是:[0, 31] 那我也需要有一个 int(32位) 类型的空间.
  4. 假设 n == 33 , 需要存储的范围是:[0, 32] 那我也需要有两个 int(32位) 类型的空间. 因为我第一个 int 类型的只能存放 [0, 31], 必须开辟下一个 int 空间用来存储 32 这个整数.
  5. 所以 1 / 32(向上取整) == 1, 2 / 32(向上取整) == 1, 33 / 32(向上取整) == 2.

总结:我们需要向上取整.

向上取整用代码实现如下:

原理:只是涉及到 a 和 b 的值(前提是 a, b 都是非负数), 利用这个式子:(a+b-1)/b

  1. 要是 a == k * b + 小余数, 不管这个余数是多少, 之后进行 + (b - 1), 都能实现向上取整的效果.
  2. 要是 a == k * b, 也能实现向上取整的效果, 只是没有变化.(假设 a = b = 3, 此时 a/b == 1), 没有实现向上取整的必要和意义. 但是使用上面的式子:5/3 == 1, 和原来是一样的.
public int[] set;  
  
// n个数字 : 0~n-1public Bitset(int n) {  
    // a/b如果结果想向上取整,可以写成 : (a+b-1)/b    // 前提是a和b都是非负数  
    set = new int[(n + 31) / 32];  // 这样就足以支持 n - 1 个数字了.
}

2.2.2 位图的加入操作 add 函数

  1. 我们先要找到 num 这个数字在数组的哪一个下标(位置)中, 利用:num / 32.
  2. 然后寻找到在这个位置中的哪一个 bit位:利用 num % 32.
  3. 找到之后, 将这个 bit位0 修改为 1.

举一个例子:

按照 8 个bit位来举例:
10101100 我们要将第 4 个位置的数字修改为 1 , 按照从右往左 0 ~ 31.
00010000 我们将 1 这个数字左移:(num % 32) 个 bit 位.
-------- | 进行“按位或”运算
10111100 这样就将“bit”位修改了.

实际操作代码如下:

public void add(int num) {  
    set[num / 32] |= 1 << (num % 32);  // 为了使代码更加简洁.
    // set[num / 32] 是数组中的下标, 第几个位置.
}

在这里插入图片描述

2.2.3 位图的删除操作 remove 函数

add 的操作是一样的,

  1. 我们先要找到 num 这个数字在数组的哪一个下标 (位置) 中, 利用:num / 32.
  2. 然后寻找到在这个位置中的哪一个 bit位:利用 num % 32.
  3. 找到之后, 将这个 bit位1 修改为 0.

还是举一个例子:

按照 8 个bit位来举例:
10101100 我们要将第 3 个位置的数字修改为 0 , 按照从右往左 0 ~ 31.
00001000 我们将 1 这个数字左移:(num % 32) 个 bit 位.

11110111 这里我们将这个数字进行取反操作 ~(1 << (num % 32)).
10101100 和原来的数字进行 & 操作.
-------- &
10100100 这样就将数字 1 修改为:0.

实际操作代码如下:

注意:推荐使用这个方法, 虽然用 ^ 运算也能做, 但是有缺陷,

  1. 使用 & 运算:不用进行判断, 无论需要修改的位置是不是 0, 我都能将其修改为 0.
  2. 使用 ^ 运算:需要进行判断, 若是需要修改的位置是 0, 那就修改为了 1.(会出现错误), 所以推荐下面这个使用 & 运算的方法.
public void remove(int num) {  
    set[num / 32] &= ~(1 << (num % 32));  
}

2.2.4 位图的反转操作 reverse 函数

reverse 函数实现的是:

  1. 若是一个数字在位图中, 通过 reverse 函数消除这个数字, (将对应的 bit位1 修改为 0)
  2. 若是一个数字不在位图中, 通过 reverse 函数添加这个数字, (将对应的 bit位0 修改为 1)

这个需要注意的地方和 remove 中的一样, 已经说的很明白了.

举一个例子:

11110111 我们举一个极端一点的例子:我们需要修改的是第 3 个位置的 0.(假设这个位置是 0 即:没有这个数字).
00001000 我们将 1 这个数字左移:(num % 32) 个 bit 位.
-------- ^
11111111 这样就修改好了.

11111111 我们继续利用这个例子, 修改第 3 个位置的 1, (这个位置的数字是 1 即:有这个数字)
00001000 我们将 1 这个数字左移:(num % 32) 个 bit 位.
-------- ^
11110111 这样就修改好了.

实际操作代码如下:

public void reverse(int num) {  
    set[num / 32] ^= 1 << (num % 32);  
}

2.2.5 位图的检查 contains 函数

我们判断一个数字 num 在不在位图中, 只需要判断对应的 bit位1 还是 0 就行了.

num / 32 和 num % 32 的意义就不说了,

有两种方法:

  1. 将对应数字的对应 bit位 向右移动到 0 位置, 然后和 1 进行 & 运算, 判断 结果是不是 == 1 就行
    1. 对应的代码:((set[num / 32] >> (num % 32)) & 1) == 1.
  2. 1 向左移动到对应的需要判断的 bit位 位置, 然后进行 & 运算, 判断 结果是不是 != 0 就行.
    1. 对应的代码:(set[num / 32] & (1 << (num % 32))) != 0.
public boolean contains(int num) {  
    return ((set[num / 32] >> (num % 32)) & 1) == 1;  
}

3. 对数器验证正确性

这里直接将所有的代码都打出来:

import java.util.HashSet;  
  
// 位图的实现  
// Bitset(int size)  
// void add(int num)  
// void remove(int num)  
// void reverse(int num)  
// boolean contains(int num)  
public class Code01_Bitset {  
  
    // 位图的实现  
    // 使用时num不要超过初始化的大小  
    public static class Bitset {  
       public int[] set;  
  
       // n个数字 : 0~n-1       public Bitset(int n) {  
          // a/b如果结果想向上取整,可以写成 : (a+b-1)/b          // 前提是a和b都是非负数  
          set = new int[(n + 31) / 32];  
       }  
  
       public void add(int num) {  
          set[num / 32] |= 1 << (num % 32);  
       }  
  
       public void remove(int num) {  
          set[num / 32] &= ~(1 << (num % 32));  
       }  
  
       public void reverse(int num) {  
          set[num / 32] ^= 1 << (num % 32);  
       }  
  
       public boolean contains(int num) {  
          return ((set[num / 32] >> (num % 32)) & 1) == 1;  
       }  
  
    }  
  
    // 对数器测试  
    public static void main(String[] args) {  
       int n = 1000;  
       int testTimes = 10000;  
       System.out.println("测试开始");  
       // 实现的位图结构  
       Bitset bitSet = new Bitset(n);  
       // 直接用HashSet做对比测试  
       HashSet<Integer> hashSet = new HashSet<>();  
       System.out.println("调用阶段开始");  
       for (int i = 0; i < testTimes; i++) {  
          double decide = Math.random();  
          // number -> 0 ~ n-1,等概率得到  
          int number = (int) (Math.random() * n);  
          if (decide < 0.333) {  
             bitSet.add(number);  
             hashSet.add(number);  
          } else if (decide < 0.666) {  
             bitSet.remove(number);  
             hashSet.remove(number);  
          } else {  
             bitSet.reverse(number);  
             if (hashSet.contains(number)) {  
                hashSet.remove(number);  
             } else {  
                hashSet.add(number);  
             }  
          }  
       }  
       System.out.println("调用阶段结束");  
       System.out.println("验证阶段开始");  
       for (int i = 0; i < n; i++) {  
          if (bitSet.contains(i) != hashSet.contains(i)) {  
             System.out.println("出错了!");  
          }  
       }  
       System.out.println("验证阶段结束");  
       System.out.println("测试结束");  
    }  
  
}

3.1 解释对数器的验证过程

提醒:对数器是一个极其重要的验证手段和 debug 练习方式, 所以一定要掌握, 一定要多熟悉, 多写对数器.

重点是解释对数器的验证过程:

  1. 原理是:使用哈希表和位图进行对比, 两个类能实现的功能是一样的, 所以只需要判断最后的结果是不是一样就行了.
  2. 首先设置 n 的范围, 然后设置 测试次数:testTimes. 创建位图和哈希表的实例
  3. 每一次测试都随机生成 number 的值(范围是:[0, 999]), 然后每一次都随机生成 decide 的值(范围是 [0, 1)).
  4. 然后根据 decide 的范围来判断执行哪一步操作. 这里我们设置的是,
    1. 若是 < 0.333(近似约等于 1/3 的概率), 就将 number 同时加入哈希表和位图中.
    2. 若是 >= 0.333 && < 0.666, 就将 number 从哈希表和位图中删除.
    3. 若是 >= 0.666 < 1, 就执行位图的反转操作, hashset 内置函数中没有这个功能的函数, 自己实现就行了, 比如分为两种情况:先用 hashsetcontains 函数判断是不是存在这个数字,
      1. 要是存在就删除.
      2. 要是不存在就加入.
  5. 然后进行验证过程:因为我们先设置的 n == 1000, 所以位图和哈希表中能进行存储的数字范围是:[0, 999], 所以对应的, 从 0 开始, 判断是不是存在于哈希表和位图中, 要是存在肯定是同时存在, 要是不存在肯定同时不存在, 因为返回的类型是 boolean类型, 所以只需要判断最后 true 和 false 能不能对上 就行.

4. 实现更多操作并且在线测试验证

在这里插入图片描述

// 位图的实现  
// Bitset是一种能以紧凑形式存储位的数据结构  
// Bitset(int n) : 初始化n个位,所有位都是0  
// void fix(int i) : 将下标i的位上的值更新为1  
// void unfix(int i) : 将下标i的位上的值更新为0  
// void flip() : 翻转所有位的值  
// boolean all() : 是否所有位都是1  
// boolean one() : 是否至少有一位是1  
// int count() : 返回所有位中1的数量  
// String toString() : 返回所有位的状态  
public class Code02_DesignBitsetTest {  
  
    // 测试链接 : https://leetcode-cn.com/problems/design-bitset/    
    class Bitset {  
       private int[] set;         // 初始化时需要使用的数组  
       private final int size;    // 判断到底要支持几个数字的查询, 在初始化的时候是多少, 后续就永远确定了.  
       private int zeros;         // 判断有几个不存在的:不存在的数字的数量  
       private int ones;          // 判断有几个存在的:存在的数字的数量  
       private boolean reverse;   // 是否整个位经历过反转.若是 reverse = false 说明:1 代表:存在, 0 代表:不存在.  
                                         // 若是 reverse = true 说明:1 代表:不存在, 0 代表:存在.  
                                         // 这样会非常方便, 不用整体进行遍历一遍修改.  
  
       public Bitset(int n) {  
          set = new int[(n + 31) / 32];  // 这个和原来一样, 还是向上取整.  
          size = n;            // 代表了可以进行 n 个数字的查询.  
          zeros = n;           // 因为最开始位图中全是:0, 所以设置为 n.          
          ones = 0;            // 因为最开始位图中全是:0, 所以设置为 0, 没有 1.          
          reverse = false;     // 此时没有经历过反转, 所以 reverse 是 false.       
          }  
  
       // 把i这个数字加入到位图  
       public void fix(int i) {  
          int index = i / 32;  
          int bit = i % 32;  
          if (!reverse) {  
             // 位图所有位的状态,维持原始含义  
             // 0 : 不存在  
             // 1 : 存在  
             if ((set[index] & (1 << bit)) == 0) { // 这里说明需要添加的位置 bit位 是:0.需要添加数字.  
                zeros--;   // 0 的数量要 “--”, 因为一个新的数字进来了.  
                ones++;    // 1 的数量要 “++”, 因为一个新的数字进来了.  
                set[index] |= (1 << bit);  
             }      // else 情况就说明 bits位 是 1, 不需要添加数字. 那就不用操作 else 情况了.  
          } else {   // 反转之后的情况.  
             // 位图所有位的状态,翻转了  
             // 0 : 存在  
             // 1 : 不存在  
             if ((set[index] & (1 << bit)) != 0) { // 说明这个 bit位 是 1, 不存在, 要将其修改为 0.
                zeros--;    // 注意, 我们这里是判断不存在的数量, 我们执行的操作是添加, 所以--.  
                ones++;     // 同样的, 我们这里执行的是添加操作, 所以要++.  
                set[index] ^= (1 << bit); // 我们前面已经有了判断, 这个 bit位 是 1, 所以要将其反转.  
             }  
          }  
       }  
  
       // 把i这个数字从位图中移除  
       public void unfix(int i) {  
          int index = i / 32;  
          int bit = i % 32;  
          if (!reverse) {  // 没有经历过反转.  
             if ((set[index] & (1 << bit)) != 0) {  
                ones--;  // 存在的-- 因为执行的是删除操作  
                zeros++; // 不存在的++, 因为执行的是删除操作.  
                set[index] ^= (1 << bit);  
             }  
          } else {         // 经历过反转.  
             if ((set[index] & (1 << bit)) == 0) {  
                ones--;   // 存在的-- 因为执行的是删除操作  
                zeros++;  // 不存在的++, 因为执行的是删除操作.  
                set[index] |= (1 << bit);  
             }  
          }  
       }  
  
       public void flip() {  
          reverse = !reverse;  // 直接进行反转, 省去了整个位图的遍历, 毕竟 reverse 不是 true 就是 false.          
          int tmp = zeros;  
          zeros = ones;  // 这里还需要交换 0 与 1 的值.(计数)的情况.  
          ones = tmp;  
       }  
  
       public boolean all() {  
          return ones == size; // 直接判断 1 的数量是不是等于 size.       
        }  
  
       public boolean one() {  
          return ones > 0; // 只要确定有一个位置上有 1 就行了,  
       }  
  
       public int count() {  
          return ones;    // 判断存在中的数字有几个, 直接返回 1 的个数.  
       }  
  
  
       // 这个方法就是将数组中所有的 bit位上的1 都拼起来, 拿出来一个下标为 0, 1, 2, 3 ... n 就开始拼就行.  
       public String toString() {  
          StringBuilder builder = new StringBuilder();  
          for (int i = 0, k = 0, number, status; i < size; k++) { // k 表示拿到了第一个整数.  
             number = set[k];  
             for (int j = 0; j < 32 && i < size; j++, i++) {  
                status = (number >> j) & 1;  // 将每一个状态都进行判断.  
                status ^= reverse ? 1 : 0;  // 需要判断 reverse 是 true 还是 false. 若是true, 需要统计 0, 若是 false 直接统计 1.                
                builder.append(status);  
             }  
          }  
          return builder.toString();  
       }  
    }  
  
}

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

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

相关文章

重生之我们在ES顶端相遇第 20 章 - Mapping 参数设置大全(进阶)

文章目录 0. 前言1. 前置知识 - _source2. copy_to3. doc_values4. index5. enabled6. normalizer7. null_value8. 总结 0. 前言 在基础篇&#xff0c;我们只介绍了索引 Mapping 的基本用法。 本章将深入探讨日常中较经常使用的 Mapping 其他参数配置。 不想看过程&#xff0c…

Qt 实现动态时钟

1.实现效果 2.widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE namespace

陪护小程序|护理陪护系统|陪护小程序成品

智能化&#xff0c;作为智慧医疗宏伟蓝图的基石&#xff0c;正引领着一场医疗服务的深刻变革。在这场变革的浪潮中&#xff0c;智慧医院小程序犹如璀璨新星&#xff0c;迅速崛起&#xff0c;而陪护小程序的诞生&#xff0c;更是如春风化雨&#xff0c;细腻地触及了老年病患、家…

Spring Cloud Netflix Ribbon 负载均衡详解和案例示范

1. 引言 在传统的集中式架构中&#xff0c;负载均衡器一般是放置在服务器端的&#xff0c;例如 Nginx等。随着微服务架构的兴起&#xff0c;服务实例的数量和部署地点变得更加动态和分布式&#xff0c;这使得在客户端进行负载均衡成为了一种可行且更灵活的方案。Netflix Ribbo…

【Linux】Linux命令与操作详解(二)权限管理、存储管理(磁盘分区、格式化、挂载)、进程管理、yum软件包安装

文章目录 一、前言二、权限管理2.1、用户权限2.2、权限说明2. 3、常用命令1、chmod2、chown3、chgrp 三、存储管理磁盘的分区、格式化与挂载1、新建一块硬盘2、在新建硬盘中进行分区3、格式化4、挂载 四、进程管理进程管理命令1、ps2、top3、nice 五、YUM软件包安装1、修改默认…

算法6:模拟运算

文章目录 z字形变幻外观数列数青蛙 题目均来自于力扣 z字形变幻 class Solution { public:string convert(string s, int numRows) {int n s.size();if(n < numRows || numRows 1) return s;int d 2 * numRows - 2;string res;for(int j 0; j < n; j d){res s[j]; …

Chromium 中<a> 标签href 属性实现分析c++

HTML 链接&#xff08;Anchor&#xff09;是网页之间跳转的核心部分。 HTML 使用链接与网络上的另一个文档相连。 HTML中的链接是一种用于在不同网页之间导航的元素。 链接通常用于将一个网页与另一个网页或资源&#xff08;如文档、图像、音频文件等&#xff09;相关联。 …

如何解决位置在表里的二维码的错行问题

众所周知&#xff0c;sap 里的二维码&#xff0c;在从其他形式转换成二维码之后&#xff0c;会经常有数据位置改变的情况&#xff0c;想让它老老实实待在原地很难&#xff0c; 这里要注意设置行间距&#xff0c;如果行间距不合适&#xff0c;就会导致错位

桥梁检测系统源码分享

桥梁检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision 研究…

Vue入门-指令学习-v-on

v-on 作用&#xff1a;注册事件 添加监听 提供处理逻辑 语法&#xff1a; v-on:事件名"内联语句" v-on:事件名"methods中的函数名" 注意&#xff1a;" v-on&#xff1a;"可以替换为" " v-on:click"XXX" --> cli…

CPU中的寄存器是什么以及它的工作原理是什么?

在计算机科学中&#xff0c;寄存器是数字设备中的一个重要组成部分&#xff0c;它用于存储数据和指令以快速处理。寄存器充当临时存储区&#xff0c;信息可以在这里被快速访问和操作&#xff0c;以执行复杂任务。寄存器是计算机中最基础的存储类型&#xff0c;它们在帮助机器高…

如何写出Pythonic的代码?

f-string、三元操作、各种解析式、生成器装饰器的熟练运用&#xff0c;“内库”引用和函数封装再加持PEP8&#xff0c;撰写的脚本不pythonic都难。&#x1f60e; (笔记模板由python脚本于2024年10月07日 18:03:27创建&#xff0c;本篇笔记适合特别喜欢python的coder翻阅) 【学习…

LeetCode讲解篇之377. 组合总和 Ⅳ

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 总和为target的元素组合个数 可以由 总和为target - nums[j]的元素组合个数 转换而来&#xff0c;其中j为nums所有元素的下标 而总和target - nums[j]的元素组合个数 可以由 总和为target - nums[j] - nums[k]的…

哪个牌子充电宝质量好又靠谱?年度四大热门款充电宝清单来了

​当今时代&#xff0c;智能手机已融入我们日常生活的方方面面&#xff0c;成为了不可或缺的伙伴。然而&#xff0c;随之而来的充电需求也日益增长&#xff0c;我们在选择充电宝时经常会面临困惑。面对市场上众多的充电宝品牌和型号&#xff0c;如何挑选一款性价比高、性能稳定…

机器学习框架(含实例说明)

机器学习框架是用于开发和部署机器学习模型的软件库和工具集。它们提供了一系列的算法、工具和基础设施&#xff0c;帮助开发者更高效地构建、训练和部署机器学习模型。以下是一些主要的机器学习框架及其详细介绍&#xff1a; 1. TensorFlow TensorFlow 是由Google开发的开源…

基于STM32的智能花盆浇水系统设计

引言 本项目设计了一个基于STM32的智能花盆浇水系统。该系统通过土壤湿度传感器检测土壤湿度&#xff0c;当湿度低于设定阈值时&#xff0c;自动启动水泵进行浇水。它还结合了温湿度传感器用于环境监测。该项目展示了STM32在传感器集成、自动控制和节水智能化应用中的作用。 …

【CKA】十六、监控Pod度量指标

16、监控Pod度量指标 1. 考题内容&#xff1a; 2. 答题思路&#xff1a; 题目意思是&#xff1a;找出label有namecpu-user的CPU最高的Pod&#xff0c;然后把它的名字写在已经存在的 /opt/KUTR00401/KUTR00401.txt文件里 3. 官网地址&#xff1a; https://kubernetes.io/zh-…

Python酷库之旅-第三方库Pandas(135)

目录 一、用法精讲 606、pandas.DataFrame.sparse.from_spmatrix方法 606-1、语法 606-2、参数 606-3、功能 606-4、返回值 606-5、说明 606-6、用法 606-6-1、数据准备 606-6-2、代码示例 606-6-3、结果输出 607、pandas.DataFrame.sparse.to_coo方法 607-1、语法…

操作系统 | 学习笔记 | 王道 | 3.1 内存管理概念

3 内存管理 3.1 内存管理概念 3.1.1 内存管理的基本原理和要求 内存可以存放数据&#xff0c;程序执行前需要先放到内存中才能被CPU处理—缓和cpu和磁盘之间的速度矛盾 内存管理的概念 虽然计算机技术飞速发展&#xff0c;内存容量也在不断扩大&#xff0c;但仍然不可能将所有…

DBMS-3.4 SQL(4)——存储过程和函数触发器

本文章的素材与知识来自李国良老师和王珊老师。 存储过程和函数 一.存储过程 1.语法 2.示例 &#xff08;1&#xff09; 使用DELIMITER更换终止符后用于编写存储过程语句后&#xff0c;在下次执行SQL语句时记得再使用DELIMITER将终止符再换回分号。 使用DELIMITER更换终止符…