当前位置: 首页 > news >正文

【哈希】闭散列的线性探测和开散列的哈希桶解决哈希冲突(C++两种方法模拟实现哈希表)(1)

图片名称
🎉博主首页: 有趣的中国人

🎉专栏首页: C++进阶

🎉其它专栏: C++初阶 | Linux | 初阶数据结构

在这里插入图片描述

小伙伴们大家好,本片文章将会讲解 哈希函数与哈希 之 闭散列的线性探测解决哈希冲突 的相关内容。

如果看到最后您觉得这篇文章写得不错,有所收获,麻烦点赞👍、收藏🌟、留下评论📝。您的支持是我最大的动力,让我们一起努力,共同成长!

文章目录

  • `1. 哈希概念`
  • `2. 哈希冲突`
  • `3. 解决哈希冲突的方法`
    • `3.1 线性探测的代码实现`
      • ==<font color = blue size = 5><b>⌛1、枚举类型定义状态⏳==
      • ==<font color = blue size = 5><b>⌛2、插入元素(Insert)⏳==
      • ==<font color = blue size = 5><b>⌛3、查找元素(Find)⏳==
      • ==<font color = blue size = 5><b>⌛4、删除元素(Erase)⏳==
  • `4. 字符串哈希`
    • `详谈字符串哈希的相关做法`
  • `5. 完整代码`



1. 哈希概念


哈希函数的概念⏳

哈希函数是一种将输入数据(例如字符串、数字等)转换为固定长度的输出数据的函数。这个输出通常称为哈希值或哈希码

哈希函数的特点是,对于相同的输入,它总是生成相同的输出,而且通常无法根据输出反推出输入。这种特性使得哈希函数在密码学、数据验证和数据检索等领域中非常有用。


哈希表的概念⏳

哈希表是一种数据结构,它利用哈希函数来快速定位存储和检索数据。哈希表由一个数组组成,每个数组元素称为桶( b u c k e t bucket bucket)或槽( s l o t slot slot)。当需要存储数据时,哈希函数会将数据的键( k e y key key)映射到数组中的一个位置,这个位置称为哈希值。数据被存储在这个位置对应的桶中。当需要检索数据时,哈希函数会根据键计算出哈希值,并在数组中定位到对应的桶,然后从这个桶中检索数据。

哈希表的关键之处在于,哈希函数的设计要尽可能地使得不同的键映射到不同的桶,以减少哈希冲突(多个键映射到同一个桶的情况)。但即使哈希函数设计得非常好,也无法完全避免冲突。因此,哈希表通常会使用一些方法来处理冲突,例如链表、开放寻址等。

链表法是将哈希表的每个桶设置为一个链表,当发生冲突时,将新数据添加到对应桶的链表中。开放寻址法则是在发生冲突时,顺序地寻找下一个空桶来存储数据。

哈希表的优点是能够以常量时间复杂度进行数据的插入、删除和查找操作,使得它在处理大量数据时具有高效性。常见的应用包括哈希集合、哈希映射等。


哈希函数与哈希表的例子⏳


在这里插入图片描述

  • 上图哈希函数是 h a s h ( k e y ) = k e y hash(key) = key hash(key)=key % c a p a c i t y capacity capacity
  • 通过哈希函数构造出的表称为哈希表。


2. 哈希冲突


对于两个数据元素的关键字 k i k_i ki k j k_j kj( i ! = j i != j i!=j),有 k i k_i ki != k j k_j kj,但有:Hash( k i k_i ki) == Hash( k j k_j kj),即:不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞


哈希冲突的例子⏳

在这里插入图片描述

  • 对于两个不同的输入数据 1 1 1 11 11 11,通过相同的哈希函数: h a s h ( k e y ) = k e y hash(key) = key hash(key)=key % c a p a c i t y capacity capacity
  • 计算出来的结果是相同的,这就是哈希冲突。


3. 解决哈希冲突的方法


解决哈希冲突 的两种常见的方法是:闭散列开散列


⌛闭散列的线性探测法⏳

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

  1. 线性探测

    比如下图中的场景,现在需要插入元素 44 44 44,先通过哈希函数计算哈希地址, h a s h A d d r hashAddr hashAddr 4 4 4,因此 44 44 44 理论上应该插在该位置,但是该位置已经放了值为 4 4 4 的元素,即发生哈希冲突。

    线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。
    • 插入
      • 通过哈希函数获取待插入元素在哈希表中的位置
      • 如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素
        在这里插入图片描述
    • 删除

      采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。比如删除元素 4 4 4,如果直接删除掉, 44 44 44 查找起来可能会受影响。因此线性探测采用标记的伪删除法来删除一个元素。

3.1 线性探测的代码实现

⌛1、枚举类型定义状态⏳


在实现删除的思路的时候思路如下:
  1. 根据哈希函数算出对应的哈希值;
  2. 以此位置为起始点,开始向后寻找;
  3. 如果找到与传入 k e y key key 值相等的值则查找成功;
  4. 如果查找到空还未找到,则查找失败。

但是这样删除可能会出现以下错误:

  1. 当出现哈希冲突的时候,我们尝试删除对应哈希值位置上的元素,没问题,但是删除之后再删除另一个元素时,哈希值会映射到此位置,但是此位置为空,会引发错误。
  2. 插入有哈希冲突的两个值( v 1 和 v 2 v1和v2 v1v2),插入第一个值的时候没问题,插入的位置为 l o c 1 loc1 loc1,假设此位置后面有一个元素 x x x ,在插入第二个元素的时候,会沿着顺序查找到为空的位置 l o c 2 loc2 loc2 。插入完成。
    • 如果此时我们要删除 x x x,那么此位置就变为空;
    • 接着删除 v 1 v1 v1,没问题;
    • 删除 v 2 v2 v2 的时候,就会出现问题。

因此我们需要有一个标记来记录每个节点的状态:

代码:

enum Status
{EXIST,DELETE,EMPTY
};template<class K, class V>
struct HashData
{Status _status = EMPTY;pair<K, V> _kv;
};

⌛2、插入元素(Insert)⏳


插入元素思路:

  1. 根据哈希函数计算插入的位置;
  2. 如果此位置的状态不为存在,直接插入;
  3. 如果此位置的状态为存在,向后查找,找到第一个状态不等于存在(空或者删除)的位置,进行插入。

Insert代码(version 1):

bool Insert(const pair<K, V>& kv)
{// 哈希函数计算位置size_t hashi = kv.first % _table.size();// 状态为存在就加加while (_table[hashi]._status == EXIST){++hashi;hashi %= _table.size();}// 出循环说明找到状态不为存在的位置,进行插入_table[hashi]._kv = kv;_table[hashi]._status = EXIST;++_n;return true;
}

⌛2.1、负载因子(load_factor)⏳

我们可以思考一下什么时候进行扩容呢?

首先看一下负载因子的定义:

在这里插入图片描述

  • 负载因子: l o a d f a c t o r = n ÷ t a b l e . s i z e ( ) loadfactor = n ÷ table.size() loadfactor=n÷table.size()

我们这边控制:当负载因子超过 0.7 0.7 0.7 的时候进行扩容,扩容思路如下:

  1. 新定义一个HashTable的对象newht,开的空间大小为两倍的原始大小( n e w s i z e = 2 ∗ t a b l e . s i z e ( ) newsize = 2 * table.size() newsize=2table.size());
  2. 遍历原始HashTable对象中的成员变量_table,如果状态为存在,则调用newhtInsert函数;
  3. 如果状态不为存在,则继续往后加加;
  4. 直到走到原始HashTable中的成员变量_table_table.size()位置。
  5. 对两个_table进行交换:_table.swap(newht._table)

扩容代码:

if (_n * 10 / _table.size() >= 7)
{// 新空间大小size_t newsize = 2 * _table.size();// 定义一个新对象HashTable<K, V, Func> newHT(newsize);for (size_t i = 0; i < _table.size(); ++i){// 如果状态为存在则在新的对象中进行插入if (_table[i]._status == EXIST)newHT.Insert(_table[i]._kv);}// 交换两个表_table.swap(newHT._table);
}

Insert代码(version 2):

bool Insert(const pair<K, V>& kv)
{if (_n * 10 / _table.size() >= 7){size_t newsize = 2 * _table.size();HashTable<K, V, Func> newHT(newsize);for (size_t i = 0; i < _table.size(); ++i){if (_table[i]._status == EXIST)newHT.Insert(_table[i]._kv);}_table.swap(newHT._table);}size_t hashi = kv.first % _table.size();while (_table[hashi]._status == EXIST){++hashi;hashi %= _table.size();}_table[hashi]._kv = kv;_table[hashi]._status = EXIST;++_n;return true;
}

⌛3、查找元素(Find)⏳


查找元素的思路:

  1. 首先根据哈希函数计算出哈希值(映射位置);
  2. 以此位置为起始点,向后寻找,直到找到为空的位置;
  3. 如果在此之间找到与所给的值相等的位置,返回此位置的地址;
    • 注意这里要判断一下找到的节点的状态是否为删除,如果是删除状态,那就直接跳过;
    • 如果不是删除才可以返回此节点的地址。
  4. 如果一直到空还未找到,说明没有哈希表中没有此元素,返回空指针nullptr

查找代码:

HashData<K, V>* Find(const K& key)
{// 根据哈希函数计算位置size_t hashi = key % _table.size();// 如果状态不为空则继续向后查找while (_table[hashi]._status != EMPTY){// 找到了并且状态不为删除,则返回此位置的地址if (_table[hashi]._status != DELETE &&_table[hashi]._kv.first == key){return &_table[hashi];}// 找不到则继续向后找else{++hashi;hashi %= _table.size();}}// 找到空还未找到返回空指针return nullptr;
}

⌛4、删除元素(Erase)⏳


删除元素的思路:

  1. 调用查找函数,看是否有此元素;
  2. 如果找到此元素,直接对此位置的状态置为DELETE
    • 这就是为什么在查找的时候要判断一下找到的那个节点的位置是否是删除状态;
    • 如果是删除状态并且返回,可能会造成重复删除的可能。
  3. 如果未找到,则返回false

删除代码:

bool Erase(const K& key)
{HashData<K, V>* ret = Find(key);if (ret == nullptr)return false;else{ret->_status = DELETE;return true;}
}


4. 字符串哈希


那当我们 插入字符串 的时候应该 用什么样的哈希函数来解决值与位置的对应关系 来实现哈希表呢?

  1. 当我们插入数字的类型,例如:double、float、int、 char、unsigned用的是一种类型的哈希函数
  2. 当我们插入字符串类型string的时候用的是另一种类型的哈希函数
  3. 🔎遇到这种情况的时候我们一般用仿函数来解决问题!!!🔍

因此我们要加一个仿函数的模板参数:class HashFunc

对于数字类型的仿函数代码:

template<class K>
struct Hash
{size_t operator()(const K& key){// 强转即可return (size_t)key;}
};

对于string类型的仿函数代码:

这里先写一下,待会再细谈:

struct StringFunc
{size_t operator()(const string& str){size_t ret = 0;for (auto& e : str){ret *= 131;ret += e;}return ret;}
};

由于string类型的哈希我们经常用,因此可以用模板的特化,并将此模板用缺省参数的形式传递,这样我们就不用在每次用的时候传入仿函数了。

template<class K>
struct Hash
{size_t operator()(const K& key){return (size_t)key;}
};template<>
struct Hash<string>
{size_t operator()(const string& str){size_t ret = 0;for (auto& e : str){ret *= 131;ret += e;}return ret;}
};

详谈字符串哈希的相关做法


我们用的哈希函数要尽量减少哈希冲突,因此在我们实现字符串转数字的时候也要尽量避免重复数据的出现次数,实际上有很多解决方法,这里有一篇文章,有兴趣的可以看一下:

🎨链接: 字符串哈希算法


其中讲了好几种哈希算法,并进行了很多测试:

在这里插入图片描述

其中效果最好的就是BKDR字符串哈希算法,由于在Brian KernighanDennis Ritchie《The C Programming Language》一书被展示而得名,是一种简单快捷的hash算法,也是Java目前采用的字符串的Hash算法(累乘因子为31)。

下面代码来自于上述链接:

template<class T>  
size_t BKDRHash(const T *str)  
{  register size_t hash = 0;  while (size_t ch = (size_t)*str++)  {         hash = hash * 131 + ch;   // 也可以乘以31、131、1313、13131、131313..  // 有人说将乘法分解为位运算及加减法可以提高效率,如将上式表达为:hash = hash << 7 + hash << 1 + hash + ch;  // 但其实在Intel平台上,CPU内部对二者的处理效率都是差不多的,  // 我分别进行了100亿次的上述两种运算,发现二者时间差距基本为0(如果是Debug版,分解成位运算后的耗时还要高1/3);  // 在ARM这类RISC系统上没有测试过,由于ARM内部使用Booth's Algorithm来模拟32位整数乘法运算,它的效率与乘数有关:  // 当乘数8-31位都为1或0时,需要1个时钟周期  // 当乘数16-31位都为1或0时,需要2个时钟周期  // 当乘数24-31位都为1或0时,需要3个时钟周期  // 否则,需要4个时钟周期  // 因此,虽然我没有实际测试,但是我依然认为二者效率上差别不大          }  return hash;  
}  


5. 完整代码


🎨博主gitee链接: Jason-of-carriben 闭散列哈希
#pragma once
#include <iostream>
#include <vector>
using namespace std;namespace open_adress
{enum Status{EXIST,DELETE,EMPTY};template<class K, class V>struct HashData{Status _status = EMPTY;pair<K, V> _kv;};struct StringFunc{size_t operator()(const string& str){size_t ret = 0;for (auto& e : str){ret *= 131;ret += e;}return ret;}};template<class K>struct Hash{size_t operator()(const K& key){return (size_t)key;}};template<>struct Hash<string>{size_t operator()(const string& str){size_t ret = 0;for (auto& e : str){ret *= 131;ret += e;}return ret;}};template<class K, class V, class Func = Hash<K>>class HashTable{public:HashTable(size_t n = 10){_table.resize(n);}bool Insert(const pair<K, V>& kv){HashData<K, V>* ret = Find(kv.first);if (ret != nullptr)return false;if (_n * 10 / _table.size() >= 7){size_t newsize = 2 * _table.size();HashTable<K, V, Func> newHT(newsize);for (size_t i = 0; i < _table.size(); ++i){if (_table[i]._status == EXIST)newHT.Insert(_table[i]._kv);}_table.swap(newHT._table);for (auto& data : _table){if (data._status == EXIST)++_n;}}Func hf;size_t hashi = hf(kv.first) % _table.size();while (_table[hashi]._status == EXIST){++hashi;hashi %= _table.size();}_table[hashi]._kv = kv;_table[hashi]._status = EXIST;++_n;return true;}HashData<K, V>* Find(const K& key){Func hf;size_t hashi = hf(key) % _table.size();while (_table[hashi]._status != EMPTY){if (_table[hashi]._status != DELETE &&_table[hashi]._kv.first == key){return &_table[hashi];}else{++hashi;hashi %= _table.size();}}return nullptr;}bool Erase(const K& key){HashData<K, V>* ret = Find(key);if (ret == nullptr)return false;else{ret->_status = DELETE;return true;}}private:vector<HashData<K, V>> _table;size_t _n = 0;};void HashTest1(){HashTable<int, int> ht;int arr[] = { 10001, 91, 72, 55, 63, 97, 80 };for (auto& e : arr){ht.Insert(make_pair(e, e));}ht.Insert(make_pair(93, 93));}void TestHT3(){//HashTable<string, int, StringHashFunc> ht;HashTable<string, int> ht;ht.Insert(make_pair("sort", 1));ht.Insert(make_pair("left", 1));ht.Insert(make_pair("insert", 1));/*cout << StringHashFunc()("bacd") << endl;cout << StringHashFunc()("abcd") << endl;cout << StringHashFunc()("aadd") << endl;*/}/*void test_map1(){string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉","苹果","草莓", "苹果","草莓" };unordered_map<string, int> countMap;for (auto& e : arr){countMap[e]++;}cout << countMap.load_factor() << endl;cout << countMap.max_load_factor() << endl;cout << countMap.size() << endl;cout << countMap.bucket_count() << endl;cout << countMap.max_bucket_count() << endl;for (auto& kv : countMap){cout << kv.first << ":" << kv.second << endl;}cout << endl;}*/}

相关文章:

【哈希】闭散列的线性探测和开散列的哈希桶解决哈希冲突(C++两种方法模拟实现哈希表)(1)

&#x1f389;博主首页&#xff1a; 有趣的中国人 &#x1f389;专栏首页&#xff1a; C进阶 &#x1f389;其它专栏&#xff1a; C初阶 | Linux | 初阶数据结构 小伙伴们大家好&#xff0c;本片文章将会讲解 哈希函数与哈希 之 闭散列的线性探测解决哈希冲突 的相关内容。 如…...

四川农业大学Java实训项目圆满收官,汇智知了堂引领学子实践创新

近日&#xff0c;四川农业大学与汇智知了堂共同举办的Java实训项目正式迎来了项目汇报阶段。本次实训是汇智知了堂在高等教育领域深化校企合作、推动产教融合的一次重要实践&#xff0c;旨在为广大学子提供一个将理论知识与实际操作相结合的平台。 在实训过程中&#xff0c;汇…...

JavaScript的当前时间设置及Date的运算

作者:私语茶馆 1.场景描述 如下图,在HTML刚加载时,需要将开始时间设置为默认当前时间,结束时间设置为当前时间后7天的时间。手工填写时间时,时间段不超过30天。 这里涉及到两个技术点: 1)Input Date的当前时间设置 2)date的运算 由于是动态修改HTML,所以采用…...

网络安全管理制度

一、总则 目的&#xff1a;本制度旨在保障组织内部网络系统的安全、稳定运行&#xff0c;保护组织的信息资产不受损害&#xff0c;确保业务的连续性和数据的完整性。适用范围&#xff1a;本制度适用于组织内部所有使用网络系统的部门、员工及第三方合作伙伴。 二、网络安全管理…...

零基础,想做一名网络安全工程师,该怎么学习?

​ 相比IT类的其它岗位&#xff0c;网络工程师的学习方向是比较明亮的。想要成为网络工程师&#xff0c;华为认证就是最好的学习方法。而网络工程师的从零开始学习就是从华为认证的初级开始学起&#xff0c;也就是HCIA&#xff0c;也就是从最基本的什么是IP地址、什么是交换机这…...

【大模型部署】在C# Winform中使用文心一言ERNIE-3.5 4K 聊天模型

【大模型部署】在C# Winform中使用文心一言ERNIE-3.5 4K 聊天模型 前言 今天来写一个简单的ernie-c#的例子&#xff0c;主要参考了百度智能云的例子&#xff0c;然后自己改了改&#xff0c;学习了ERNIE模型的鉴权方式&#xff0c;数据流的格式和简单的数据解析&#xff0c;实…...

【Unity】Unity项目转抖音小游戏(三)资源分包,抖音云CDN

业务需求&#xff0c;开始接触一下抖音小游戏相关的内容&#xff0c;开发过程中记录一下流程。 使用资源分包可以优化游戏启动速度&#xff0c;是抖音小游戏推荐的一种方式&#xff0c;抖音云也提供存放资源的CDN服务 抖音云官方文档&#xff1a;https://developer.open-douyi…...

SQLite查询优化

文章目录 1. 引言2. WHERE子句分析2.1. 索引项使用示例 3. BETWEEN优化4. OR优化4.1. 将OR连接的约束转换为IN运算符4.2. 分别评估OR约束并取结果的并集 5. LIKE优化6. 跳跃扫描优化7. 连接7.1. 手动控制连接顺序7.1.1. 使用 SQLITE_STAT 表手动控制查询计划7.1.2. 使用 CROSS …...

UE4编辑器End键Actor贴近地面

void UXXXEditorFunctionLibrary::SnapToFloor(AActor* Actor) { if (Actor) { Actor->Modify(); GEditor->SnapObjectTo(FActorOrComponent(Actor), false, false, false, false); Actor->InvalidateLightingCache(); Actor->UpdateComponentTransforms(); } }...

2024儿科常用心理评估量表汇总,附详细操作步骤与评定标准

在社会的快速发展以及家庭教育模式的转变下&#xff0c;儿童心理健康问题正逐步成为公众瞩目的焦点。焦虑症、抑郁症、适应障碍等儿科常见的症状&#xff0c;不仅对孩子的身心健康构成威胁&#xff0c;更可能在他们的学习旅程和社交互动中制造重重障碍。 儿科医师常用评估量表…...

Python 脚本化 Git 操作:简单、高效、无压力

前言 如何判定此次测试是否达标&#xff0c;代码覆盖率是衡量的标准之一。前段时间&#xff0c;利用fastapi框架重写了覆盖率统计服务&#xff0c;核心其实就是先获取全量代码覆盖率&#xff0c;然后通过diff操作统计增量代码覆盖率&#xff0c;当然要使用diff操作&#xff0c…...

手搓顺序表(C语言)

目录 SeqList.h SeqList.c 头插尾插复用任意位置插入 头删尾删复用任意位置删除 SLtest.c 测试示例 顺序表优劣分析 SeqList.h //SeqList.h#pragma once#include <stdio.h> #include <assert.h> #include <stdlib.h> #define IN_CY 3typedef int S…...

一文搞懂oracle事务提交以及脏数据落盘的原则

本文基于oracle 19c 做事务提交以及oracle脏数据落盘的相关解读 第一章 相关进程及组件介绍&#xff1a; 1.LGWR&#xff1a; 重做日志条目在系统全局区域 &#xff08;SGA&#xff09; 的重做日志缓冲区中生成。LGWR 按顺序将重做日志条目写入重做日志文件。如果数据库具有…...

OceanBase:列存储

目录 1、列存储的定义 1、默认创建列存表 3、指定创建列存表 4、指定创建列存行存冗余表 5、行、列存储查询测试 1、列存储的定义 行存储&#xff08;Row-based Storage&#xff09;&#xff1a;行存储是以行为单位进行组织和存储数据。在这一模式下&#xff0c;数据库将…...

Rust:WIndows 环境下交叉编译 Linux 平台程序

在Windows下交叉编译Rust程序以在x86_64位的CentOS操作系统上运行&#xff0c;你需要遵循几个步骤来设置交叉编译环境并编译你的程序。以下是一个大致的指南&#xff1a; 1. 安装Rust和Cargo 首先&#xff0c;确保你已经在Windows上安装了Rust和Cargo。你可以从Rust官方网站下…...

从零学爬虫:使用比如说说解析网页结构

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、网页结构概述 示例&#xff1a;查看网页结构 三、使用比如说说解析网页 1.…...

C#数据类型变量、常量

一个变量只不过是一个供程序操作的存储区的名字。 在 C# 中&#xff0c;变量是用于存储和表示数据的标识符&#xff0c;在声明变量时&#xff0c;您需要指定变量的类型&#xff0c;并且可以选择性地为其分配一个初始值。 在 C# 中&#xff0c;每个变量都有一个特定的类型&…...

Java高级面试问题及答案

Java高级面试问题及答案 问题1: 请描述Java内存模型(JMM)及其在并发编程中的重要性。 探讨过程&#xff1a; 在并发编程中&#xff0c;多个线程之间如何协调对共享变量的访问是一个核心问题。Java内存模型定义了一组规则&#xff0c;来确保在多线程环境中对共享变量的修改能够…...

出现 Transaction rolled back because it has been marked as rollback-only 解决方法

目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 用户反馈的Bug如下所示: Transaction rolled back because it has been marked as rollback-only截图如下: 浏览器终端同样显示: 2. 原理分析 错误表明,在事务的生命周期内,遇到了某个异常或条件,导致该事务被标记…...

数据结构算法题day03

数据结构算法题day03 题目 题目 2.设计一个高效算法&#xff0c;将顺序表L的所有元素逆置&#xff0c;要求算法的空间复杂度为O(1)算法思想&#xff1a; 1、常规的解法&#xff1a; Void reverse (sqlist &L){Elemtype temp; //辅助变量for(i 0,i < L.length; i){temp…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...