哈希表(c语言)

文章目录

  • 哈希表
    • 哈希表知识点
      • 哈希表概念
        • 负载因子
      • 哈希表的优缺点
      • 哈希冲突
      • 哈希函数
        • 常见哈希函数
      • 处理哈希冲突
        • 开放定址法
          • 线性探测
          • 二次探测
          • 链地址法
    • 哈希表的实现
      • 哈希表的核心:==HashMap==
      • 核心函数:从创建到销毁
        • 创建哈希表:hashmap_create()
        • 销毁哈希表:hashmap_destroy
        • 插入键值对:hashmap_put
        • 查找键对应的值:hashmap_get
        • 删除键值对:hashmap_remove
        • 拷贝函数:strdup1(const char* s)
      • 哈希函数
      • 测试函数
      • 完整代码

哈希表

哈希表知识点

哈希表概念

哈希表是一种以关联方式存储数据的数据结构。在哈希表中,数据以数组格式存储,其中每个数据值都有自己的唯一的索引值。哈希表也叫散列表,是根据关键码值(Key Value)而直接进行访问的数据结构。它通过把关键码之值映射到表中一个位置来访问记录。

简单来讲其实数组就是一张哈希表,如同所示:
在这里插入图片描述
在这里插入图片描述

负载因子

概念:衡量哈希表填充程度的核心指标,直接关联数据结构的存储效率与操作性能。其数值由已存元素数量除以哈希表总容量得出,合理控制负载因子能有效平衡空间利用率和操作速度。

假设哈希表中已经映射存储了N个值,哈希表的大小为M,那么负载因子为:

a = N/M

哈希表的优缺点

优点

简化了比较过程,效率大大提高。

缺点

1.散列技术不适合集合中重复元素很多的情形,因为这样的话同样的key;

2.散列技术不适合范围查找 也不适合查找最大值,最小值.这些都无法从散列函数中计算出来

3.散列函数需要很好的设计,应该保证简单 均匀 存储效率高

哈希冲突

不同关键字通过相同哈希函数计算出相同的哈希地址,该现象称为哈希冲突哈希碰撞

如果此时再将元素66插入到上面的哈希表,因为元素66通过哈希函数计算得到的哈希地址与元素6相同,那么就会产生哈希冲突。
在这里插入图片描述

哈希函数

基本概念:是将哈希表中元素的关键值映射为元素存储位置的函数

常见哈希函数

1.直接定址法(常用)

取关键字的某个线性函数为散列地址:Hash(Key) = A*Key + B

优点:简单,效率很高

缺点:需要事先知道关键字的分布情况

使用场景:适合查找数据比较小且连续的情况

2.除留余树法(常用)

设散列表中允许的地址数为 m,取一个不大于 m,但最接近或者等于 m 的质数 p 作为除数,按照哈希函数:Hash(key) = key % p (p <= m),将关键码转换成哈希地址

优点:使用场景广泛,不受限制

缺点:存在哈希冲突,需要解决哈希冲突,哈希冲突越多,效率下降越厉害

3.乘法散列法

乘法散列法对哈希表大小 M MM 没有要求,他的大致思路分为两步:

【第一步】用关键字key 乘上常数 A (0<A<1),并抽取出key × A 的小数部分。

【第二步】再用M 乘以k × A 的小数部分,再向下取整。

4.全域散列法

如果存在一个恶意的对手,他针对我们提供的散列函数,特意构造出一个发生严重冲突的数据集,比如,让所有关键字全部落入同一个位置中。这种情况是可以存在的,只要散列函数是公开且确定的,就可以实现此攻击。

给散列函数增加随机性,攻击者就无法找出确定可以导致最坏情况的数据,这种方法叫做全域散列

处理哈希冲突

开放定址法

在开放定址法中所有的元素都放到哈希表里,当一个关键字key用哈希函数计算出的位置冲突了,则按照某种规则找到一个没有存储数据的位置进行存储。这里的规则有二种:线性探测二次探测链地址法

线性探测

从发生冲突的位置开始,一次线性向后探测,直到寻找到下一个没有存储数据的位置为止,如果走到哈希表尾,则回绕到哈希表头的位置

查找公式:hashi = hash(key) % N + i

其中:

  • hashi:冲突元素通过线性探测后得到的存放位置
  • hash(key) % N:通过哈希函数对元素的关键码进行计算得到的位置
  • N:哈希表的大小
  • i 从 1、2、3、4…一直自增

示例:

现在有这样一组数据集合 {10, 25, 3, 18, 54, 999} 我们用除留余数法将它们插入到表长为 10 的哈希表中
在这里插入图片描述
现在需要插入新元素 44,先通过哈希函数计算哈希地址,hashAddr 为 4,因此 44 理论上应该插在该位置,但是该位置已经放了值为 4 的元素,即发生哈希冲突,然后我们使用线性探测的计算公式hashi = 44% 10 + 1 = 5,但是下表为5的位置已经被占用了,那么继续计算hashi = 44% 10 + 2 = 6下标为6的位置没有被占用,那么就把44插入该位置
在这里插入图片描述
如果随着哈希表中数据的增多,产生哈希冲突的可能性也随着增加,假设现在要把 33 进行插入,那么会连续出现四次哈希冲突,我们将数据插入到有限的空间,那么空间中的元素越多,插入元素时产生冲突的概率也就越大,冲突多次后插入哈希表的元素,在查找时的效率必然也会降低。因此,哈希表当中引入了负载因子(载荷因子):

  • 散列表的载荷因子定义为:α=填入表中的元素个数/散列表的长度
  • 负载因子越大,产出冲突的概率越高,增删查改的效率越低
  • 负载因子越小,产出冲突的概率越低,增删查改的效率越高

假设我们现在将哈希表的大小改为 20,再把上面的数据重新插入,可以看到完全没有产生的哈希冲突:
在这里插入图片描述
小贴士:

  • 线性探测优点:实现非常简单
  • 缺点:一旦发生哈希冲突,所有的冲突连在一起,容易产生数据堆积
二次探测

二次探测与线性探测类似但它地址的位置更稀松,找下一个空位置的方法为:hashi = hash(key) % N + i^2(i = 1、2、3、4…)

  • hashi:冲突元素通过线性探测后得到的存放位置
  • hash(key) % N:通过哈希函数对元素的关键码进行计算得到的位置
  • N:哈希表的大小。
链地址法

拉链法中,哈希表中每个键对应的值都为一个链表的头节点,当发生哈希冲突时,新的键值对会被插入到相应的链表中。如下图所示,字符x发生哈希冲突时将其加入到链表头节点中:
在这里插入图片描述

哈希表的实现

哈希表的核心:HashMap

哈希表的本质是“数组+链表”的组合。每个链表节点保存具体的键值对

//哈希桶节点(链表节点)
typedef struct node_s
{KeyType key;          // 键ValueType value;      // 值struct node_s* next;  // 指向下一个节点的指针
}KeyValueNode;//哈希表核心结构体
typedef struct 
{KeyValueNode* buckets[HASHMAP_CAPACITY];//哈希桶数组uint32_t hash_seed; // 哈希种子(随机化)
}HashMap;

关键点:

  • buckets数组:确定了哈希表的整个大小,每一个元素是一个链表的头节点。当不同的键使用哈希函数映射的同一位置时,使用单链表用于处理哈希冲突(链地址法)
  • hash_seed:哈希函数中的随机参数,通过time(NULL)在哈希表初始化时生成,作用是扩大地址范围,避免哈希冲突

核心函数:从创建到销毁

创建哈希表:hashmap_create()

参数:无

返回值:HashMap*

作用:创建需要初始化的数组和随机种子

HashMap* hashmap_create()
{HashMap* map = (HashMap*)malloc(sizeof(HashMap));if(!map){printf("hashmap_create failed\n");return NULL;}map->hash_seed = (uint32_t)time(NULL);//初始化随机种子return map;
}

关键点:

使用malloc动态申请分配哈希表内存

销毁哈希表:hashmap_destroy

参数:HashMap*

返回值:空

作用:销毁哈希表,释放所有节点的内存

void hashmap_destroy(HashMap* map)
{//判断哈希表是否已经创建,如果没有创建就直接返回,不需要释放内存if(!map)    return;for(int i = 0;i < HASHMAP_CAPACITY;i++){KeyValueNode* current = map->buckets[i];while(current){KeyValueNode* next = current->next;//保存下一个节点free(current->key);//释放键free(current->value);//释放值free(current);//释放当前节点的内存current = next;//移动下一个节点}}free(map);//释放哈希表本身的内存
}

关键点:

  • 遍历链表:通过current指针逐个释放链表节点
  • 内存释放顺序:先释放节点的键和值,在释放节点本身,最后释放哈希表
插入键值对:hashmap_put

参数:HashMap*KeyTypeValueType

返回值:ValueType

作用:插入新的键值对或更新值

ValueType hashmap_put(HashMap* map,KeyType key,ValueType val)
{if(!map || !key || !val)    return NULL;//参数校验uint32_t index = hash(key,map->hash_seed);//计算获得的位置//查找键是否已经存在(更新值)KeyValueNode* current = map->buckets[index];while(current){if(strcmp(current->key,key) == 0){free(current->value);current->value = strdup1(val);return current->value; // 返回更新后的值}current  = current->next;}//键不存在,创建新节点KeyValueNode* new_node = (KeyValueNode*)malloc(sizeof(KeyValueNode));//判断新节点是否创建成功if(!new_node){perror("Failed to allocate memory for new node"); }new_node->key = strdup1(key);new_node->value = strdup1(val);new_node->next = map->buckets[index];map->buckets[index] = new_node; // 将新节点插入到链表头部}

关键点:

  • 哈希计算:通过哈希函数将键转换为索引
  • 哈希冲突:如果键已经存在,更新对应的值;
  • 性能优化:使用头插法插入(无需遍历到链表尾部),比尾插法更高效
查找键对应的值:hashmap_get

参数:HashMap*KeyType

返回值:ValueType

作用:根据键查询对应的值

ValueType hashmap_get(HashMap* map,KeyType key)
{if(!map || !key) return NULL;uint32_t index = hash(key,map->hash_seed);//计算获得的位置//遍历链表查找键KeyValueNode* current = map->buckets[index];while(current){if(strcmp(current->key,key) == 0){return current->value; // 返回找到的值}current = current->next;}return NULL;
}

关键点:

  • 参数校验:避免空指针导致的崩溃
  • 线性查找:时间复杂度为O(1)
删除键值对:hashmap_remove

参数:HashMap*KeyType

返回值:bool

作用:删除指定的键值对

bool hashmap_remove(HashMap* map,KeyType key)
{if(!map || !key) return false;uint32_t index = hash(key,map->hash_seed);KeyValueNode* current = map->buckets[index];KeyValueNode* prev = NULL;while(current){if(strcmp(current->key,key) == 0){//处理链表链接if(prev){prev->next = current->next;}else{map->buckets[index] = current->next;//删除头节点}//释放内存free(current->key);free(current->value);free(current);return true;}prev = current; //记录前一个节点current = current->next; //移动到下一个节点}return false; //未找到键
}

关键点:

  • 头节点删除:若目标点是链表头,直接更新桶的头指针为current->next
  • 中间/尾部删除:通过前一个节点prev的next指针跳过当前节点
拷贝函数:strdup1(const char* s)

参数:const char*

返回值:char*

作用:复制字符串

char* strdup1(const char* s)
{size_t len = strlen(s) + 1;char* p = (char*)malloc(len);if(p)memcpy(p,s,len);return p;
}

哈希函数

static uint32_t hash(const char* key,uint32_t seed)
{uint32_t hash_val = 0;while(*key){hash_val = (hash_val* 31) + (uint32_t)*key++;}return (hash_val^seed) % HASHMAP_CAPACITY;
}

设计思想:

  • 多项式哈希:hash_val = hash_val * 31 + *key 是经典的字符串哈希算法(31是质数,能有效减少冲突)
  • 随机种子:通过seed(时间戳)对哈希值取异或,避免相同输入生成相同哈希值(防御碰撞攻击)
  • 取模运算:将哈希值映射到[0, HASHMAP_CAPACITY-1]的范围,确定桶的位置

测试函数

#include "hash.h"int main()
{//创建哈希表HashMap* map = hashmap_create();//判断哈希表是否创建成功if(!map){printf("create hashmap failed\n");return -1;}//插入键值对hashmap_put(map,"name","libai");hashmap_put(map,"age","66");hashmap_put(map,"location","changan");hashmap_put(map,"title","shixian");hashmap_put(map,"hobby","drink");//查询键对应的值ValueType name =  hashmap_get(map,"name");if(name){printf("name:%s\n",name);}ValueType title = hashmap_get(map,"title");if(title){printf("title:%s\n",title);}//删除键值对hashmap_remove(map,"age");ValueType age = hashmap_get(map,"age");if(age == NULL){printf("age has been removed\n");}//销毁哈希表hashmap_destroy(map);}

输出结果:
在这里插入图片描述

完整代码

hash.h

#ifndef HASH_H
#define HASH_H#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define HASHMAP_CAPACITY 10//哈希表容量固定为10typedef char* KeyType;//键类型:字符串指针
typedef char* ValueType;//值类型:字符串指针//哈希桶节点(链表节点)
typedef struct node_s
{KeyType key;          // 键ValueType value;      // 值struct node_s* next;  // 指向下一个节点的指针
}KeyValueNode;//哈希表核心结构体
typedef struct 
{KeyValueNode* buckets[HASHMAP_CAPACITY];//哈希桶数组uint32_t hash_seed; // 哈希种子(随机化)
}HashMap;//初始化哈希表
HashMap* hashmap_create();//销毁哈希表
void hashmap_destroy(HashMap* map);//插入/更新键值对
ValueType hashmap_put(HashMap* map,KeyType key,ValueType val);//查询键对应的值
ValueType hashmap_get(HashMap* map,KeyType key);//删除键值对
bool hashmap_remove(HashMap* map,KeyType key);#endif 

hash.c

#include "hash.h"//深拷贝字符串
char* strdup1(const char* s)
{size_t len = strlen(s) + 1;char* p = (char*)malloc(len);if(p)memcpy(p,s,len);return p;
}//创建哈希表
HashMap* hashmap_create()
{HashMap* map = (HashMap*)malloc(sizeof(HashMap));if(!map){printf("hashmap_create failed\n");return NULL;}map->hash_seed = (uint32_t)time(NULL);//初始化随机种子return map;
}//销毁哈希表(释放所有内存)
void hashmap_destroy(HashMap* map)
{//判断哈希表是否已经创建,如果没有创建就直接返回,不需要释放内存if(!map)    return;for(int i = 0;i < HASHMAP_CAPACITY;i++){KeyValueNode* current = map->buckets[i];while(current){KeyValueNode* next = current->next;//保存下一个节点free(current->key);//释放键free(current->value);//释放值free(current);//释放当前节点的内存current = next;//移动下一个节点}}free(map);//释放哈希表本身的内存
}//哈希函数
static uint32_t hash(const char* key,uint32_t seed)
{uint32_t hash_val = 0;while(*key){hash_val = (hash_val* 31) + (uint32_t)*key++;}return (hash_val^seed) % HASHMAP_CAPACITY;
}//插入/更新键值对
ValueType hashmap_put(HashMap* map,KeyType key,ValueType val)
{if(!map || !key || !val)    return NULL;//参数校验uint32_t index = hash(key,map->hash_seed);//计算获得的位置//查找键是否已经存在(更新值)KeyValueNode* current = map->buckets[index];while(current){if(strcmp(current->key,key) == 0){free(current->value);//防止内存泄漏current->value = strdup1(val);return current->value; // 返回更新后的值}current  = current->next;}//键不存在,创建新节点KeyValueNode* new_node = (KeyValueNode*)malloc(sizeof(KeyValueNode));//判断新节点是否创建成功if(!new_node){perror("Failed to allocate memory for new node"); }new_node->key = strdup1(key);new_node->value = strdup1(val);new_node->next = map->buckets[index];map->buckets[index] = new_node; // 将新节点插入到链表头部}//查询键对应的值
ValueType hashmap_get(HashMap* map,KeyType key)
{if(!map || !key) return NULL;uint32_t index = hash(key,map->hash_seed);//计算获得的位置//遍历链表查找键KeyValueNode* current = map->buckets[index];while(current){if(strcmp(current->key,key) == 0){return current->value; // 返回找到的值}current = current->next;}return NULL;
}//删除键值对
bool hashmap_remove(HashMap* map,KeyType key)
{if(!map || !key) return false;uint32_t index = hash(key,map->hash_seed);KeyValueNode* current = map->buckets[index];KeyValueNode* prev = NULL;while(current){if(strcmp(current->key,key) == 0){//处理链表链接if(prev){prev->next = current->next;}else{map->buckets[index] = current->next;//删除头节点}//释放内存free(current->key);free(current->value);free(current);return true;}prev = current; //记录前一个节点current = current->next; //移动到下一个节点}return false; //未找到键
}

main.c

#include "hash.h"int main()
{//创建哈希表HashMap* map = hashmap_create();//判断哈希表是否创建成功if(!map){printf("create hashmap failed\n");return -1;}//插入键值对hashmap_put(map,"name","libai");hashmap_put(map,"age","66");hashmap_put(map,"location","changan");hashmap_put(map,"title","shixian");hashmap_put(map,"hobby","drink");//查询键对应的值ValueType name =  hashmap_get(map,"name");if(name){printf("name:%s\n",name);}ValueType title = hashmap_get(map,"title");if(title){printf("title:%s\n",title);}//删除键值对hashmap_remove(map,"age");ValueType age = hashmap_get(map,"age");if(age == NULL){printf("age has been removed\n");}//销毁哈希表hashmap_destroy(map);}

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

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

相关文章

【Canvas与旗帜】条纹版大明三辰旗

【成图】【代码】<!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>十三条纹版大明三辰旗 Draft1</title><style type"text/…

【Java】空指针(NullPointerException)异常深度攻坚:从底层原理到架构级防御,老司机的实战经验

写Java代码这些年&#xff0c;空指针异常&#xff08;NullPointerException&#xff09;就像甩不掉的影子。线上排查问题时&#xff0c;十次有八次最后定位到的都是某个对象没处理好null值。但多数人解决问题只停留在加个if (obj ! null)的层面&#xff0c;没从根本上想过为什么…

【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 主页-评论用户时间占比环形饼状图实现

大家好&#xff0c;我是java1234_小锋老师&#xff0c;最近写了一套【NLP舆情分析】基于python微博舆情分析可视化系统(flaskpandasecharts)视频教程&#xff0c;持续更新中&#xff0c;计划月底更新完&#xff0c;感谢支持。今天讲解主页-评论用户时间占比环形饼状图实现 视频…

Redis面试精讲 Day 5:Redis内存管理与过期策略

【Redis面试精讲 Day 5】Redis内存管理与过期策略 开篇 欢迎来到"Redis面试精讲"系列的第5天&#xff01;今天我们将深入探讨Redis内存管理与过期策略&#xff0c;这是面试中经常被问及的核心知识点。对于后端工程师而言&#xff0c;理解Redis如何高效管理内存、处…

ICMPv6报文类型详解表

一、错误报文类型&#xff08;Type 1-127&#xff09;Type值名称Code范围触发条件示例典型用途1Destination Unreachable0-60: 无路由到目标1: 通信被管理员禁止2: 地址不可达3: 端口不可达4: 分片需要但DF标志设置5: 源路由失败6: 目的地址不可达网络故障诊断2Packet Too Big0…

配置nodejs

第一步确认 node.exe 和 npm 存在 例如安装目录D:\nodejs检查是否存在以下文件&#xff1a; node.exenpm.cmdnpx.cmd 第二步&#xff1a;添加环境变量 PATH 图形化操作步骤&#xff08;Windows&#xff09;&#xff1a; 右键「此电脑」→「属性」点击左侧 「高级系统设置」弹出…

MySQL的命令行客户端

MySQL中的一些程序&#xff1a;MySQL在安装完成的时候&#xff0c;一般都会包含如下程序&#xff1a;在Linux系统下&#xff0c;通过/usr/bin目录下&#xff0c;可以通过命令查看&#xff1a;以下是常用的MySQL程序&#xff1a;程序名作用mysqldMySQL的守护进程即MySQL服务器&a…

C# 值类型与引用类型的储存方式_堆栈_

目录 值类型 引用类型 修改stu3的值 stu也被修改了 为什么? &#xff08;对象之间&#xff09; 值类型中&#xff0c;值全在栈中单独存储&#xff0c;变量之间不会影响 结构体中&#xff0c;结构体全在栈中&#xff0c;结构体与结构体之间也不会相互影响 静态资源区 值类…

解锁永久会员的白噪音软件:睡眠助手

如今的年轻人压力普遍较大&#xff0c;学会解压至关重要。这期就为大家推荐一款优秀的白噪音软件&#xff0c;在压力大时听听&#xff0c;能起到不错的解压效果。 睡眠助手 文末获取 这款软件的特别版本十分出色&#xff0c;知晓的人不多。它已解锁永久会员&#xff0c;无需登…

uniapp使用css实现进度条带动画过渡效果

一、效果 二、实现原理 1.uni.createAnimation 动画函数 2.初始化uni.createAnimation方法 3.监听值的变化调用动画执行方法 三、代码 1.实现方式比较简单&#xff0c;目前是vue3的写法&#xff0c;vue2只需要稍微改动即可 <template><view class"layout_progre…

高级分布式系统调试:调试的科学与 USE 方法实战

高级分布式系统调试:调试的科学与 USE 方法实战 前言:从“救火”到“探案” 当一个复杂的分布式系统出现“灰色故障”——例如“服务有时会变慢”、“偶尔出现超时错误”——我们该从何处着手?随机地查看 Grafana 仪表盘,或者漫无目的地 tail -f 日志,往往效率低下,甚至…

栈算法之【有效括号】

目录 LeetCode-20题 LeetCode-20题 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每…

大模型——Data Agent:超越 BI 与 AI 的边界

Data Agent:超越 BI 与 AI 的边界 1. 数据工具的演进路径 在数据分析领域,技术工具经历了多个阶段的演进。这些演进不仅反映了技术的进步,也体现了用户需求和使用场景的变化。 Excel 时代:告别手工作业,陷入“表格泥潭“,早期数据分析依赖 Excel,实现基础数据记录、计…

数据空间技术在智慧水库管理平台中的赋能

数据空间技术在智慧水库管理平台中的赋能&#xff1a;设备到应用的数据传输优化 数据空间技术为智慧水库管理平台提供了革命性的数据传输、处理和安全保障能力。以下是数据空间技术在设备到应用数据传输过程中的全面赋能方案&#xff1a; 数据空间赋能架构设计 #mermaid-svg-R2…

SpringBoot学习路径二--Spring Boot自动配置原理深度解析

SpringBoot最核心的功能就是自动装配&#xff0c;Starter作为SpringBoot的核心功能之一&#xff0c;基于自动配置代码提供了自动配置模块及依赖的能力&#xff0c;让软件集成变得简单、易用。使用SpringBoot时&#xff0c;我们只需引I人对应的Starter&#xff0c;SpringBoot启动…

音视频中一些常见的知识点

1. GCC是如何进行带宽评估的 GCC(Google Congestion Control)是一种专为实时音视频传输设计的拥塞控制算法,它主要通过发送端和接收端的协同工作来进行带宽评估。具体过程如下: 接收端处理 计算延迟梯度:接收端通过统计数据包到达时间的变化,即RTT(往返时间)波动,来计…

STM32硬件I2C的注意事项

文章目录软件模拟I2C硬件的实现方式最近在研究I2C的屏幕使用。有两种使用方式&#xff0c;软件模拟I2C、硬件HAL使用I2C。软件模拟I2C 发送数据是通过设置引脚的高低电平实现的。 /*引脚配置*/ #define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_6, (BitAction)(x)) #de…

Python捕获异常

Python捕获异常主要通过try-except语句实现&#xff0c;以下是核心语法和使用场景&#xff1a;一、基础捕获结构try: # 可能引发异常的代码 result 10 / 0 except ZeroDivisionError: # 处理特定异常 print("除数不能为零") 二、捕获多种异常try: # 可能引发…

Scala 和 Spark 大数据分析(六)

原文&#xff1a;annas-archive.org/md5/39eecc62e023387ee8c22ca10d1a221a 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十三章&#xff1a;我的名字是贝叶斯&#xff0c;朴素贝叶斯 “预测是非常困难的&#xff0c;尤其是当它涉及未来时” -尼尔斯玻尔 机器学…

【kubernetes】-6污点与污点容忍

文章目录污点与污点容忍1、 污点&#xff08;taint&#xff09;2、操作命令3、污点容忍4、污点扩展污点与污点容忍 1、 污点&#xff08;taint&#xff09; 污点是节点的属性&#xff0c;用于排斥一类特定的 Pod。通过污点&#xff0c;可以避免 Pod 被调度到不合适的节点上 …