数据结构C语言描述8(图文结合)--哈希、哈希冲突、开放地址法、链地址法等实现
前言
- 这个专栏将会用纯C实现常用的数据结构和简单的算法;
- 有C基础即可跟着学习,代码均可运行;
- 准备考研的也可跟着写,个人感觉,如果时间充裕,手写一遍比看书、刷题管用很多,这也是本人采用纯C语言实现的原因之一;
- 欢迎收藏 + 关注,本人将会持续更新。
文章目录
- 什么是哈希
- 哈希构造函数
- 哈希解决冲突方法
- 开放地址寻址法
- 链地址法
- 数组哈希案例实现
- 封装
- 创建哈希
- 得到哈希映射值
- 插入数据
- 哈希查找
- 总代码
- 链表哈希案例实现
- 封装
- 插入
- 查找
- 总代码
什么是哈希
📘 概念:哈希结构(Hash Table)也被称为散列表,是一种用于实现字典(Dictionary)的数据结构。
重点:散列表,这个刷过算法的人应该都知道这个数据结果,散列表的特点就是查找特别快,典型应用:判断A集合是否存在B集合;字典结构,我感觉学过Python
或者JS
都很清楚这个特点了;
🔴 重点概念
- 哈希结构通过键
Key
映射到值Value
的过程称为哈希,映射学过数学应该都知道这个概念,函数就是典型的映射关系; - 哈希函数:将键映射到一个存储位置的函数;
⏫ 用处
哈希用处很多,最核心的应用是查找,哈希查找速度很快,如:用数组作为哈希结构容器,假设我们要存储一段数据(1, 5, 100, 10000, 100000),想要查找最快,找到数组下标即可,但是
这个最大值很大(100000),如果申请这么大的内存,储存量却很少,这就造成了内存浪费,但是用哈希结构的一些方法,可以很大的节省时间,数组也是一种哈希结构,这个后面我们会讲。
哈希构造函数
🏡 概念 哈希构造函数(Hash Constructor)是哈希结构中的一种函数,用于将键(Key)映射到哈希表中的位置。哈希构造函数通常是一个确定性函数,即对于相同的键,哈希构造函数总是返回相同的哈希值。哈希构造函数的设计非常关键,它直接影响哈希结构的查找、插入和删除等操作的效率。
重点:
- 哈希函数:将键值映射到哈希表中位置;
- 哈希构造函数通常是一个确定性函数,即对于相同的键,哈希构造函数总是返回相同的哈希值,键与射的值相互对于,具有唯一性;
- 哈希构造函数的设计非常关键,但是这个一般我感觉需要结合业务场景,根据不同数据从而选取不同的哈希函数,下面会有一些常用的哈希函数。
🌓 哈希构造函数的设计需要满足以下几个要求:
- 一致性:对于相同的键,哈希构造函数应该总是返回相同的哈希值。
- 均匀性:哈希构造函数应该尽可能均匀地将键映射到哈希表中的位置,以避免哈希冲突。
- 高效性:哈希构造函数的计算时间应该尽可能短,以提高哈希结构的操作效率。
🌌 哈希构造函数的设计方法有很多种,常见的方法包括:
-
直接寻址法(Direct Addressing):将键直接作为哈希表中的位置,适用于键的范围比较小的情况。
-
除留余数法(Division Method):将键除以一个不大于哈希表大小的质数,然后取余数作为哈希值,h*(*k)= k mod m,m 是哈希表的大小,即输出哈希值的范围,这个也是最常用的,一般来说,m不能过于靠近2的幂。
-
乘法哈希法(Multiplicative Hashing):将键乘以一个常数A(0<A<1),然后取乘积的小数部分乘以哈希表大小作为哈希值。
-
一次探测法(Linear Probing):当发生哈希冲突时,依次向后探测空桶,直到找到空桶或者遍历整个哈希表。
-
双重哈希法(Double Hashing):使用两个不同的哈希函数,当发生哈希冲突时,依次使用两个哈希函数计算出新的哈希值,直到找到空桶或者遍历整个哈希表。
🌾 当然还有其他,注意:像除留余数法,为什么需要除以不大于哈希表最大大小的质数,这是是有严格的数学证明,可以减少哈希冲突概率,记住即可。
哈希解决冲突方法
这个有很多,如:开放地址寻址法、再次哈希法、链地址法、建立公共溢出区,这里只讲解,开放地址寻址法和链地址法。
开放地址寻址法
开发地址寻址法就是在发生哈希冲突的时候,在哈希映射那个点往后寻找,寻找到一个空位即可。
链地址法
链地址法就是用一个链表,当发生哈希冲突的时候,在哈希映射那个位置,拉一条链表,将数据填入链表中。
数组哈希案例实现
封装
// 这里假设存储的数据是这个,key是自己抽象出来的
typedef struct Data {int key;char buffer[20];
}Data;typedef struct Hash {Data** data;int p;int count;
}Hash;
创建哈希
Hash* create_hash(int p)
{Hash* hash = (Hash*)calloc(1, sizeof(Hash));assert(hash);hash->data = (Data**)calloc(p, sizeof(Data));assert(hash->data);hash->p = p;return hash;
}
得到哈希映射值
// 如果相同key值,则覆盖
int get_index(Hash* hash, Data data)
{assert(hash);int pos = data.key % hash->p;int curPos = pos;do {if (hash->data[curPos] == NULL || hash->data[curPos]->key == data.key) {return curPos;}curPos = (curPos + 1) % hash->p;} while (curPos != pos);return -1; // 找不到,则当前满了,这里假设数据能够存储
}
插入数据
void push(Hash* hash, Data data)
{assert(hash);int pos = get_index(hash, data);if (pos == -1) {return;}if (hash->data[pos] == NULL) {Data* newData = (Data*)calloc(1, sizeof(Data));assert(newData);hash->data[pos] = newData;// 内存拷贝memcpy(hash->data[pos], &data, sizeof(data));}else {if (hash->data[pos]->key == data.key) {// 字符串拷贝strcpy(hash->data[pos]->buffer, data.buffer);}}hash->count++;
}
哈希查找
循环查找一圈看是否能找到。
Data* search(Hash* hash, int key)
{assert(hash);int pos = key % hash->count;int curPos = pos;do {if (hash->data[curPos] == NULL) {return NULL;}if (hash->data[curPos]->key == key) {return hash->data[curPos];}curPos = (curPos + 1) % hash->p;} while (pos != curPos);return NULL;
}
总代码
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>/*f(key) = key / p;
*/// 这里假设存储的数据是这个,key是自己抽象出来的
typedef struct Data {int key;char buffer[20];
}Data;typedef struct Hash {Data** data;int p;int count;
}Hash;Hash* create_hash(int p)
{Hash* hash = (Hash*)calloc(1, sizeof(Hash));assert(hash);hash->data = (Data**)calloc(p, sizeof(Data));assert(hash->data);hash->p = p;return hash;
}// 如果相同key值,则覆盖
int get_index(Hash* hash, Data data)
{assert(hash);int pos = data.key % hash->p;int curPos = pos;do {if (hash->data[curPos] == NULL || hash->data[curPos]->key == data.key) {return curPos;}curPos = (curPos + 1) % hash->p;} while (curPos != pos);return -1; // 找不到,则当前满了,这里假设数据能够存储
}void push(Hash* hash, Data data)
{assert(hash);int pos = get_index(hash, data);if (pos == -1) {return;}if (hash->data[pos] == NULL) {Data* newData = (Data*)calloc(1, sizeof(Data));assert(newData);hash->data[pos] = newData;// 内存拷贝memcpy(hash->data[pos], &data, sizeof(data));}else {if (hash->data[pos]->key == data.key) {// 字符串拷贝strcpy(hash->data[pos]->buffer, data.buffer);}}hash->count++;
}void print_hash(Hash* hash)
{assert(hash);for (int i = 0; i < hash->p; i++) {if (hash->data[i] == NULL) {printf("NULL\n");}else {printf("%d %s\n", hash->data[i]->key, hash->data[i]->buffer);}}
}Data* search(Hash* hash, int key)
{assert(hash);int pos = key % hash->count;int curPos = pos;do {if (hash->data[curPos] == NULL) {return NULL;}if (hash->data[curPos]->key == key) {return hash->data[curPos];}curPos = (curPos + 1) % hash->p;} while (pos != curPos);return NULL;
}int size(Hash* hash)
{assert(hash);return hash->count;
}bool empty(Hash* hash)
{assert(hash);return hash->count == 0;
}int main()
{Hash* hash = create_hash(11);Data data[8] = { 1,"小美",11,"小芳",25,"花花",38,"coco",45,"小帅",88,"baby",65,"小丽",75,"小小" };for (int i = 0; i < 8; i++) {push(hash, data[i]);}print_hash(hash);Data* sdata = search(hash, 45);printf("search: %d %s\n", sdata->key, sdata->buffer);return 0;
}
链表哈希案例实现
封装
// 数据封装
typedef struct Data {int key;char buffer[20];
}Data;// 横向链表节点封装(解决冲突节点)
typedef struct LNode {Data data;struct LNode* next;
}LNode;// 纵向链表节点封装
typedef struct SNode {Data data;struct SNode* snext;LNode* lnext; // 解决冲突链表
}SNode;// 哈希封装
typedef struct Hash {SNode* headNode;int p; // 余数int count; // 存储的元素个数
}Hash;LNode* create_lnode(Data data)
{LNode* node = (LNode*)calloc(1, sizeof(LNode));assert(node);node->data = data;return node;
}SNode* create_snode(Data data)
{SNode* node = (SNode*)calloc(1, sizeof(SNode));assert(node);node->data = data;return node;
}Hash* create_hash(int p)
{Hash* hash = (Hash*)calloc(1, sizeof(Hash));assert(hash);hash->p = p;return hash;
}
插入
这个确实规矩有点多,如:
- 第一步得到哈希映射值
- 如果在主链表中已经存在了,则在主链表映射位置拉一条链表,头插(这里是)
- 如果主链表中不存在,则插入主链表中
// 插入
// 规则有点多
void push(Hash* hash, Data data)
{assert(hash);// 创建节点SNode* new_node = create_snode(data);int pos = data.key % hash->p;// 没有,则纵向链表生成if (hash->headNode == NULL) {hash->headNode = new_node;}else { // 检查SNode* insert = hash->headNode;SNode* prevInsert = NULL;// if (insert->data.key % hash->p > pos) {new_node->snext = hash->headNode;hash->headNode = new_node;hash->count++;}else {while (insert != NULL && (insert->data.key % hash->p < pos)) {prevInsert = insert;insert = insert->snext;}// if (insert == NULL) {prevInsert->snext = new_node;hash->count++;}else if (insert->data.key % hash->p == pos) {//if (insert->data.key == data.key) {strcpy(insert->data.buffer, data.buffer);}else {LNode* curNode = insert->lnext;if (curNode == NULL) {insert->lnext = create_lnode(data);hash->count++;}else {// 横向插入,头插while (curNode != NULL && curNode->data.key != data.key) {curNode = curNode->next;}// 没有,头插if (curNode == NULL) {LNode* temp = create_lnode(data);temp->next = insert->lnext;insert->lnext = temp;hash->count++;}else { // 有,覆盖strcpy(curNode->data.buffer, data.buffer);}}}}else {new_node->snext = insert;prevInsert->snext = new_node;hash->count++;}}}}
查找
这个查找分为两个步骤:
- 主链表中找(纵向)
- 在映射值的那个链表中找
// 打印
void print_hash(Hash* hash)
{assert(hash);SNode* rTemp = hash->headNode;while (rTemp) {printf("key: %d, value: %s", rTemp->data.key, rTemp->data.buffer);if (rTemp->lnext != NULL) {printf(" 【");LNode* lTemp = rTemp->lnext;while (lTemp != NULL) {printf("key: %d, value: %s", lTemp->data.key, lTemp->data.buffer);lTemp = lTemp->next;}printf(" 】");// 看清楚这个是行的数据}rTemp = rTemp->snext;printf("\n");}}// 查找
enum MatchType
{NoMatch, // 没有RowMatch, // 纵找到ColMatch // 横找到
};typedef struct Match {int type;union {LNode* lnode;SNode* snode;};
}Match;Match search(Hash* hash, Data data)
{assert(hash);Match res = { 0 };int pos = data.key % hash->p;SNode* rTemp = hash->headNode;while (rTemp) {if (pos == rTemp->data.key % hash->p) {if (data.key == rTemp->data.key) {res.type = RowMatch;res.snode = rTemp;return res;}else {LNode* lTemp = rTemp->lnext;while (lTemp) {if (lTemp->data.key == data.key) {res.type = ColMatch;res.lnode = lTemp;return res;}lTemp == lTemp->next;}}}rTemp = rTemp->snext;}return res;
}void print_search(Match match)
{switch (match.type){case NoMatch:printf("没有找到\n");break;case RowMatch:printf("找到了,key: %d, value: %s\n", match.snode->data.key, match.snode->data.buffer);break;case ColMatch:printf("找到了,key: %d, value: %s\n", match.lnode->data.key, match.lnode->data.buffer);break;}
}
总代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>// 链表实现哈希,使用链表法解决冲突,要注意的是封装会比较多
// 假设不处理重复值// 数据封装
typedef struct Data {int key;char buffer[20];
}Data;// 横向链表节点封装(解决冲突节点)
typedef struct LNode {Data data;struct LNode* next;
}LNode;// 纵向链表节点封装
typedef struct SNode {Data data;struct SNode* snext;LNode* lnext; // 解决冲突链表
}SNode;// 哈希封装
typedef struct Hash {SNode* headNode;int p; // 余数int count; // 存储的元素个数
}Hash;LNode* create_lnode(Data data)
{LNode* node = (LNode*)calloc(1, sizeof(LNode));assert(node);node->data = data;return node;
}SNode* create_snode(Data data)
{SNode* node = (SNode*)calloc(1, sizeof(SNode));assert(node);node->data = data;return node;
}Hash* create_hash(int p)
{Hash* hash = (Hash*)calloc(1, sizeof(Hash));assert(hash);hash->p = p;return hash;
}// 插入
// 规则有点多
void push(Hash* hash, Data data)
{assert(hash);// 创建节点SNode* new_node = create_snode(data);int pos = data.key % hash->p;// 没有,则纵向链表生成if (hash->headNode == NULL) {hash->headNode = new_node;}else { // 检查SNode* insert = hash->headNode;SNode* prevInsert = NULL;// if (insert->data.key % hash->p > pos) {new_node->snext = hash->headNode;hash->headNode = new_node;hash->count++;}else {while (insert != NULL && (insert->data.key % hash->p < pos)) {prevInsert = insert;insert = insert->snext;}// if (insert == NULL) {prevInsert->snext = new_node;hash->count++;}else if (insert->data.key % hash->p == pos) {//if (insert->data.key == data.key) {strcpy(insert->data.buffer, data.buffer);}else {LNode* curNode = insert->lnext;if (curNode == NULL) {insert->lnext = create_lnode(data);hash->count++;}else {// 横向插入,头插while (curNode != NULL && curNode->data.key != data.key) {curNode = curNode->next;}// 没有,头插if (curNode == NULL) {LNode* temp = create_lnode(data);temp->next = insert->lnext;insert->lnext = temp;hash->count++;}else { // 有,覆盖strcpy(curNode->data.buffer, data.buffer);}}}}else {new_node->snext = insert;prevInsert->snext = new_node;hash->count++;}}}}// 打印
void print_hash(Hash* hash)
{assert(hash);SNode* rTemp = hash->headNode;while (rTemp) {printf("key: %d, value: %s", rTemp->data.key, rTemp->data.buffer);if (rTemp->lnext != NULL) {printf(" 【");LNode* lTemp = rTemp->lnext;while (lTemp != NULL) {printf("key: %d, value: %s", lTemp->data.key, lTemp->data.buffer);lTemp = lTemp->next;}printf(" 】");// 看清楚这个是行的数据}rTemp = rTemp->snext;printf("\n");}}// 查找
enum MatchType
{NoMatch, // 没有RowMatch, // 纵找到ColMatch // 横找到
};typedef struct Match {int type;union {LNode* lnode;SNode* snode;};
}Match;Match search(Hash* hash, Data data)
{assert(hash);Match res = { 0 };int pos = data.key % hash->p;SNode* rTemp = hash->headNode;while (rTemp) {if (pos == rTemp->data.key % hash->p) {if (data.key == rTemp->data.key) {res.type = RowMatch;res.snode = rTemp;return res;}else {LNode* lTemp = rTemp->lnext;while (lTemp) {if (lTemp->data.key == data.key) {res.type = ColMatch;res.lnode = lTemp;return res;}lTemp == lTemp->next;}}}rTemp = rTemp->snext;}return res;
}void print_search(Match match)
{switch (match.type){case NoMatch:printf("没有找到\n");break;case RowMatch:printf("找到了,key: %d, value: %s\n", match.snode->data.key, match.snode->data.buffer);break;case ColMatch:printf("找到了,key: %d, value: %s\n", match.lnode->data.key, match.lnode->data.buffer);break;}
}int main()
{Hash* hash = create_hash(11);Data data[8] = { 1,"小美",11,"小芳",25,"花花",38,"coco",42,"小帅",88,"baby",66,"小丽",77,"小小" };for (int i = 0; i < 8; i++) {push(hash, data[i]);}print_hash(hash);printf("********************\n");Match search_res = search(hash, data[3]);print_search(search_res);return 0;
}
相关文章:

数据结构C语言描述8(图文结合)--哈希、哈希冲突、开放地址法、链地址法等实现
前言 这个专栏将会用纯C实现常用的数据结构和简单的算法;有C基础即可跟着学习,代码均可运行;准备考研的也可跟着写,个人感觉,如果时间充裕,手写一遍比看书、刷题管用很多,这也是本人采用纯C语言…...

自动化立体库安全使用管理制度完整版
导语 大家好,我是社长,老K。专注分享智能制造和智能仓储物流等内容。欢迎大家到本文底部评论区留言。 新书《智能物流系统构成与技术实践》人俱乐部 完整版文件和更多学习资料,请球友到知识星球【智能仓储物流技术研习社】自行下载。 以下是《…...

云打印之拼多多打印组件交互协议
拼多多打印组件交互协议相关介绍如下: 1、打印组件下载地址 http://meta.pinduoduo.com/api/one/app/v1/lateststable?appIdcom.xunmeng.pddprint&platformwindows&subTypemain 2、socket连接端口 如果是http的话,端口是5000 socket new …...
TCP 演进之路:软硬件跷跷板与新征程
今天依旧是与 TCP 相关的一个短评。 先看软硬件间的胶着。晶体管诞生以来,硬件一直在突飞猛进发展,后来这个事被摩尔定律正则化,人们开始可以预测未来,但即便如此,软件依然跟不上来,不过几年,老…...
React最小状态管理Jotai
Jotai 状态管理 1. 简介 Jotai 是一个基于原子 atom 概念的 React 状态管理库,它提供了简单且灵活的方式来管理应用状态, 而且非常轻量, 大厂用的非常多。 JotaiRedux适合单个页面,多次用到的属性适合全局公共属性超级轻量(与use…...

计算机网络 —— 网络编程(TCP)
计算机网络 —— 网络编程(TCP) TCP和UDP的区别TCP (Transmission Control Protocol)UDP (User Datagram Protocol) 前期准备listen (服务端)函数原型返回值使用示例注意事项 accpect (服务端)函数原型返回…...
字玩FontPlayer开发笔记4 性能优化 首屏加载时间优化
字玩FontPlayer开发笔记4 性能优化 首屏加载时间优化 字玩FontPlayer是笔者开源的一款字体设计工具,使用Vue3 ElementUI开发,源代码: github: https://github.com/HiToysMaker/fontplayer gitee: https://gitee.com/toysmaker/fontplayer …...
RabbitMQ案例
1. 导入依赖 <!--AMQP依赖,包含RabbitMQ--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency> 发送消息 注入RabbitTemplate Autowired RabbitT…...
智能工厂的设计软件 应用场景的一个例子:为AI聊天工具添加一个知识系统 之13 方案再探之4:特定于领域的模板 之 div模型(完整版)
前景提要 整个“方案再探”篇 围绕着如何将项目附件文档中Part 1 部分中给出的零散问题讨论整理、重组为一个结构化的设计文档。为此提出了讨论题目: 特定于领域的模板--一个三套接的hoc结构 它是本项目actors 的剧本原型。其地位: 祖传代码脚本模板…...

WebRtc02:WebRtc架构、目录结构、运行机制
整体架构 WebRtc主要分为三层: CAPI层:外层调用Session管理核心层:包括视频引擎、音频引擎、网络传输 可由使用者重写视频引擎:编解码器、视频缓存、视频增强音频引擎:编解码器、音频缓存、回音消除、降噪传输&#x…...

数据结构复习 (顺序查找,对半查找,斐波那契查找,插值查找,分块查找)
查找(检索): 定义:从给定的数据中找到对应的K 1,顺序查找: O(n)的从前向后的遍历 2,对半查找,要求有序 从中间开始查找,每次检查中间的是否正确,不正确就…...

el-input输入框需要支持多输入,最后传输给后台的字段值以逗号分割
需求:一个输入框字段需要支持多次输入,最后传输给后台的字段值以逗号分割 解决方案:结合了el-tag组件的动态编辑标签 那块的代码 //子组件 <template><div class"input-multiple-box" idinputMultipleBox><div>…...

C# 枚举格式字符串
总目录 前言 当前文章为 C# 中的格式设置(格式化字符串) 大全 中的一个小章节。 一、概述 1. 基本信息 可以使用 Enum.ToString 方法,新建表示枚举成员的数字值、十六进制值或字符串值的字符串对象。枚举格式说明符不区分大小写。 二、自定义数字格式说明符详解…...

【51单片机-零基础chapter1】
安装软件(配套的有,不多赘述) 1.管理员身份运行keil和破解软件kegen 将CID代码复制粘贴到 一定要管理员方式,不然会error 插入板子 我的电脑,管理 1.如果是拯救者,查看端口,如果没有则显示隐藏 2.苹果不知道,好像不可以 3.其他电脑在"其他设备找" (注:本人在校已…...

记录:导出功能:接收文件流数据进行导出(vue3)
请求接口:一定要加responseType: blob 后端返回数据: api.js export function export() {return request({url: dev/api/export,method: get,responseType: blob,//一定要加}) } vue: import {export} from /api// 导出 const exportTab…...

基于Spring Boot + Vue3实现的在线汽车保养维修预约管理系统源码+文档
前言 基于Spring Boot Vue3实现的在线汽车保养维修预约管理系统是一种前后端分离架构的应用,它结合了Java后端开发框架Spring Boot和现代JavaScript前端框架Vue.js 3.0的优势。这样的系统可以为汽车服务站提供一个高效的平台来管理客户的预约请求 技术选型 系统…...

PHP框架+gatewayworker实现在线1对1聊天--接收消息(7)
文章目录 接收消息的原理接收消息JavaScript代码 接收消息的原理 接收消息,就是接受服务器转发的客户端消息。并不需要单独创建函数,因为 ws.onmessage会自动接收消息。我们需要在这个函数里进行处理。因为初始化的时候,已经处理的init类型的…...

18.1、网络安全策略分类 流程 内容
目录 网络安全测评概况网络安全测评类型—基于测评目标分类网络安全测评类型—基于实施方式分类网络安全测评类型—基于测评对象保密性分类网络安全等级保护测评内容网络安全测评流程与内容 网络安全测评概况 网络安全测评,它是指参照一定的标准规范要求࿰…...
深入理解连接池:从数据库到HTTP的优化之道
在现代应用开发中,高效的资源管理是关键,其中连接池(Connection Pool)技术起到了至关重要的作用。本文将带你深入了解连接池的概念及其在数据库和HTTP通信中的应用,结合 JDBC 与 Druid 的关系,以及 HttpURL…...

【2025最新计算机毕业设计】基于SpringBoot+Vue智慧养老医护系统(高质量源码,提供文档,免费部署到本地)【提供源码+答辩PPT+文档+项目部署】
作者简介:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流。✌ 主要内容:🌟Java项目、Python项目、前端项目、PHP、ASP.NET、人工智能…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...