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

news2024/10/7 11:49:50
图片名称
🎉博主首页: 有趣的中国人

🎉专栏首页: 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;
	}*/

}

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

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

相关文章

【Elasticsearch】Centos7安装Elasticsearch、kibana、IK分词

目录 本文安装包下载地址注意安装elasticsearch1.上传文件2.解压elasticsearch-6.3.1.tar.gz3.开启远程连接权限4.修改其他配置[root用户操作]5.重启虚拟机6.启动es7.外部访问 安装kibana-61.解压2.配置3.启动kibana4.访问5.在开发工具中做数据的增删改查操作 安装IK分词1.wind…

vue3(一):Vue3简介、创建vue3工程、Vue3中的响应式

目录 一.Vue3简介 1.性能提升 2.源码升级 3.拥抱ts 4.新特性 &#xff08;1&#xff09;Composition API&#xff08;组合API&#xff09;&#xff1a; &#xff08;2&#xff09;新的内置组件&#xff1a; &#xff08;3&#xff09;其他改变&#xff1a; 二.创建vue…

iOS推送证书过期处理

苹果推送证书的有效期都是一年&#xff0c;将要过期的时候&#xff0c;苹果官方会发邮件提醒。 一、过期 在电脑上找到并打开其它->钥匙串访问&#xff1b; 我的证书可以看到各个App的推送证书&#xff0c;如果过期了&#xff0c;显示红色X 二、重新创建 1、登陆apple开…

智能高效的IDE GoLand v2024.1全新发布 - 进一步升级AI辅助工具

GoLand 使 Go 代码的阅读、编写和更改变得非常容易。即时错误检测和修复建议&#xff0c;通过一步撤消快速安全重构&#xff0c;智能代码完成&#xff0c;死代码检测和文档提示帮助所有 Go 开发人员&#xff0c;从新手到经验丰富的专业人士&#xff0c;创建快速、高效、和可靠的…

CAS原理技术

CAS原理技术 背景介绍结构体系术语接口原理基础模式1. 首次访问集成CAS Client的应用2. 再次访问集成CAS Client的同一应用3. 访问集成CAS Client的其他应用 代理模式1. 用户在代理服务器上执行身份认证2. 通过代理应用访问其他应用上授权性资源 背景 本文内容大多基于网上其他…

GitLab项目中添加用户,并设置其角色权限等

注意&#xff1a;创建用户(new user)&#xff0c;创建完用户然后再项目邀请用户&#xff0c;选择创建过的用户 一、以管理员身份登录GitLab的WebUI并创建用户 1>.使用管理员登录GitLab 使用管理员(root)用户登录成功后&#xff0c;点击如下图所示的小扳手&#xff0c;点击…

废品回收小程序:回收市场下的商业机遇

随着当下大众环保意识的提升&#xff0c;回收行业收到了大众的重视&#xff0c;行业快速发展。在互联网信息技术的支持下&#xff0c;“互联网废品回收”得到了发展&#xff0c;依靠各种技术搭建互联网回收平台&#xff0c;连接到居民与商家&#xff0c;让回收变得更加简单高效…

小猪APP分发:让你的应用轻松上架,免费分发

你是否曾经因为应用无法顺利上架而烦恼&#xff1f;或者&#xff0c;刚刚开发好的应用找不到一个合适的平台进行分发&#xff1f;其实&#xff0c;这些问题都不再是问题&#xff0c;因为“小猪APP分发”来了&#xff01; 每个开发者都希望自己的应用能够被更多的人下载和使用&…

抖店怎么选品?抖店爆款选品思路技巧,新手直接用!

大家好&#xff0c;我是电商花花。 抖店选品永远是我们做抖店&#xff0c;做电商的核心&#xff0c;店铺想要出单&#xff0c;想要赚钱&#xff0c;我们就一定要学会怎么选品&#xff0c;怎么筛选商品。 而我们绝大多数新手并没有办法保证持续选爆款的能力&#xff0c;如果店…

2024年最新实景无人自动直播软件揭秘:降低成本,提升效率

在信息技术迅猛发展的时代&#xff0c;实景无人自动直播软件成为了各行各业的关注焦点。随着2024年的到来&#xff0c;最新的实景无人自动直播软件将以其降低成本、提升效率的特点引领行业。本文将揭秘这一创新软件的工作原理&#xff0c;并探讨其在各个领域的应用&#xff0c;…

简谈SUID提权

SUID提权 0x01什么是SUID ​ SUID (Set UID)是Linux中的一种特殊权限,其功能为用户运行某个程序时&#xff0c;如果该程序有SUID权限&#xff0c;那么程序运行为进程时&#xff0c;进程的属主不是发起者&#xff0c;而是程序文件所属的属主。但是SUID权限的设置只针对二进制可…

【ARM+Codesys案例】T3/RK3568/树莓派+Codesys枕式包装机运动控制器

枕式包装机是一种包装能力非常强&#xff0c;且能适合多种规格用于食品和非食品包装的连续式包装机。它不但能用于无商标包装材料的包装&#xff0c;而且能够使用预先印有商标图案的卷筒材料进行高速包装。同时&#xff0c;具有稳定性高、生产效率高&#xff0c;适合连续包装、…

场景题11111

关单操作&#xff1f; 优先考虑定时任务、Redissonredis、RocketMQ延迟消息实现&#xff08;订单量特别大的时候&#xff0c;不建议使用MQ&#xff09; 每个订单都有一个消息会增加资源消耗可靠性问题&#xff08;丢失&#xff09;大量的无效消息不是所有消息队列都支持一般通…

轻兔推荐 —— vfox

简介 vfox 是一个跨平台且可扩展的版本管理工具&#xff0c;终于有一个可以管理所有运行环境的工具了 - 支持一键安装 Java、Node.js、Flutter、.Net、Golang、PHP、Python等多种环境 - 支持一键切换不同版本 特点 支持Windows(非WSL)、Linux、macOS! 支持不同项目不同版本、…

2、PHP 8.1.0-dev 后门远程命令执行漏洞复现

1、青少年ctf&#xff0c;题目PHP后门 2、页面 3、bp抓包发现PHP版本为8.1.0-dev 4、尝试使用以前爆出过的漏洞&#xff08;网上查相关案例&#xff09; User-Agentt: zerodiumvar_dump(5*5); User-Agentt: zerodiumsystem("cat /flag"); 5、查找flag User-Agentt: z…

OceanBase:列存储

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

第十八节:带你梳理Vue2: Vue组件中的注意事项和特例

1. Vue组件名推荐使用驼峰命名 现在我们来看看为什么在Vue中推荐注册组件时使用驼峰写法, 在了解这个之前,相信大家应该都能明白为什么在Vue中, 局部组件的使用频率高于全局组件. 推荐使用驼峰写法也是和局部组件有关系 我们先看一个示例 <div id"app"><…

JAVA云HIS医院系统源码 云HIS运维平台源码 融合B/S版电子病历系统,支持电子病历四级,saas模式

JAVA云HIS医院系统源码 云HIS运维平台源码 融合B/S版电子病历系统&#xff0c;支持电子病历四级&#xff0c;saas模式 HIS系统就是医院信息管理系统&#xff0c;HIS系统是整个医院信息化的核心&#xff0c;门诊、住院、药房、药库等都是由HIS系统来承载起来的&#xff0c;所以…

MATLAB | 儿童节一起来玩转MATLAB叭

hey, 儿童节要到啦&#xff0c;提前祝各位大朋友&#xff0c;小朋友儿童节快乐&#xff0c;本篇将会在前半部分给出一些有趣且简短的代码&#xff0c;在后半部分展示一下我准备的游戏大礼包里面的游戏效果&#xff0c;完整代码获取方式请见文末&#xff0c;让我们一起玩转MATLA…

【SpringBoot】单元测试实战演示及心得分享

目录 1.指定测试标准 2.设计测试用例 3.测试集示例 4.跑测试集 1.指定测试标准 单元测试会用到mock和junit的内容&#xff0c;作者前文有详解&#xff0c;可移步&#xff1a; Spring Boot单元测试-CSDN博客 mockito的详细使用-CSDN博客 1.1.测哪一层&#xff1f; 以当…