【数据结构 09】哈希

哈希算法:哈希也叫散列、映射,将任意长度的输入通过散列运算转化为固定长度的输出,该输出就是哈希值(散列值)。

哈希映射是一种压缩映射,通常情况下,散列值的空间远小于输入值的空间。

哈希运算的结果称为哈希值,哈希运算是不可逆过程,即不能通过哈希值推算出原值。

哈希运算常用于加密、位图、布隆过滤,位图的作用是海量数据的标记,布隆过滤器的作用是提高海量数据查询的效率(客户端向服务端查询数据)。

一、哈希函数

HashFunc.h

#pragma once
#include <iostream>

// 仿函数
template<class K>
struct HashFunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

// 特化
template<>
struct HashFunc<std::string>
{
	size_t operator()(const std::string& str)
	{
		size_t res = 0;
		for (const auto& ch : str)
		{
			res *= 131;	// 随机数取值,避免哈希冲突
			res += ch;
		}
		return res;
	}
};

哈希表:将数据根据哈希运算得到哈希值(关键值),再根据哈希值将数据映射在表中,哈希表通常情况是一个vector容器。哈希表分为闭散列和开散列(哈希桶)。

哈希表的数据增删与红黑树差别不大,各有优劣,但是哈希表的数据查询效率远高于红黑树。

二、闭散列

#define _CRT_SECURE_NO_WARNINGS 1

#pragma
#include <iostream>
#include <vector>
#include "HashFunc.h"

enum status
{
	EMPTY,
	EXIST,
	DELETE
};

template<class K, class V>
struct CloseHashNode
{
	std::pair<K, V> _kv;
	status _status = EMPTY;
};

template<class K, class V, class Hash = HashFunc<K>>
class CloseHash
{
	typedef CloseHashNode<K, V> Data;
public:
	CloseHash()
		: _n(0)
	{
		_table.resize(10);
	}

	bool Insert(const std::pair<K, V>& kv)
	{
		if (Find(kv.first))
			return false;

		// 负载因子为0.7
		if (_n * 10 / _table.size() >= 7)
		{
			std::vector<Data> newTable;
			newTable.resize(2 * _table.size());
			for (int i = 0; i < _table.size(); ++i)
			{
				if (_table[i]._status == EXIST)
				{
					size_t pos = Hash()(_table[i]._kv.first) % newTable.size();
					while (newTable[pos]._status != EMPTY)
					{
						pos = (++pos) % newTable.size();
					}
					newTable[pos] = _table[i];
				}
			}
			_table.swap(newTable);
		}

		size_t pos = Hash()(kv.first) % _table.size();
		while (_table[pos]._status != EMPTY)
		{
			pos = (++pos) % _table.size();
		}
		_table[pos]._kv = kv;
		_table[pos]._status = EXIST;
		++_n;
		return true;
	}

	Data* Find(const K& key)
	{
		size_t pos = Hash()(key) % _table.size();
		int cnt = 0;
		while (_table[pos]._status != EMPTY && cnt != _table.size())
		{
			if (key == _table[pos]._kv.first && _table[pos]._status == EXIST)
				return &_table[pos];
			pos = (++pos) % _table.size();
			++cnt;
		}
		return nullptr;
	}

	bool Erase(const K& key)
	{
		Data* ret = Find(key);
		if (ret)
		{
			ret->_status = DELETE;
			--_n;
			return true;
		}
		else
		{
			return false;
		}
	}

private:
	std::vector<Data> _table;
	size_t _n;
};

三、开散列

开散列也称哈希桶,哈希桶的vector节点存储的是数据节点,相同哈希值的节点以链表的形式存储在同一个vector位置上,当节点数与vector容量的比值为平衡因子值(1)时,哈希桶扩容,扩容时重新遍历原表,将原表的元素重新取哈希进行映射,为了提高效率,不拷贝节点,而是改变节点的指向。

#define _CRT_SECURE_NO_WARNINGS 1

#pragma once
#include <iostream>
#include <vector>
#include "HashFunc.h"

template<class K, class V>
struct OpenHashNode
{
	std::pair<K, V> kv;
	OpenHashNode<K, V>* next;
	
	OpenHashNode(const std::pair<K, V>& x)
		: kv(x), next(nullptr)
	{}
};

template<class K, class V, class Hash = HashFunc<K>>
class OpenHash
{
	typedef OpenHashNode<K, V> Node;
public:
	OpenHash()
		: _n(0)
	{
		_table.resize(10, nullptr);
	}

	bool Insert(const std::pair<K, V>& kv)
	{
		if (Find(kv.first))
			return false;

		// 检查扩容,平衡因子为 1
		if (_n == _table.size())
		{
			std::vector<Node*> newTable;
			newTable.resize(2 * _table.size(), nullptr);

			for (int i = 0; i < _table.size(); ++i)
			{
				Node* cur = _table[i];
				while (cur)
				{
					Node* next = cur->next;

					size_t pos = Hash()(cur->kv.first) % newTable.size();
					cur->next = newTable[pos];
					newTable[pos] = cur;

					cur = next;
				}
			}

			_table.swap(newTable);
		}

		// 插入新节点
		Node* newNode = new Node(kv);
		size_t pos = Hash()(newNode->kv.first) % _table.size();
		newNode->next = _table[pos];
		_table[pos] = newNode;
		++_n;
		return true;

	}

	Node* Find(const K& key)
	{
		size_t pos = Hash()(key) % _table.size();
		Node* cur = _table[pos];
		while (cur)
		{
			if (cur->kv.first == key)
				return cur;
			cur = cur->next;
		}
		return nullptr;
	}

	bool Erase(const K& key)
	{
		Node* ret = Find(key);
		if (ret)
		{
			size_t pos = Hash()(key) % _table.size();
			Node* cur = _table[pos];
			if (cur == ret)
			{
				cur = ret->next;
				delete ret;
				ret = nullptr;
			}
			else
			{
				while (cur->next != ret)
				{
					cur = cur->next;
				}
				cur->next = ret->next;
				delete ret;
				ret = nullptr;
			}
			--_n;
			return true;
		}
		else
		{
			return false;
		}
	}

private:
	std::vector<Node*> _table;
	int _n;
};

四、测试

#define _CRT_SECURE_NO_WARNINGS 1

#include "CloseHash.h"
#include "OpenHash.h"
using namespace std;

void TestCloseHash()
{
	cout << "CloseHash: " << endl << endl;
	CloseHash<int, int> hash;

	int arr[] = { 34, 36, 12, 54, 5, 22, 65, 32, 13, 4, 1, 52 };
	for (auto& e : arr)
	{
		hash.Insert(make_pair(e, e));
	}

	cout << hash.Find(12) << endl;
	cout << hash.Find(22) << endl;
	cout << hash.Find(32) << endl;
	cout << hash.Find(42) << endl;
	cout << hash.Find(52) << endl;

	cout << endl;
	hash.Erase(32);
	cout << hash.Find(12) << endl;
	cout << hash.Find(22) << endl;
	cout << hash.Find(32) << endl;
	cout << hash.Find(42) << endl;
	cout << hash.Find(52) << endl;
}

void TestOpenHash()
{
	cout << endl << endl << "OpenHash: " << endl << endl;
	OpenHash<int, int> hash;

	int arr[] = { 34, 36, 12, 54, 5, 22, 65, 32, 13, 4, 1, 52 };
	for (auto& e : arr)
	{
		hash.Insert(make_pair(e, e));
	}

	cout << hash.Find(12) << endl;
	cout << hash.Find(22) << endl;
	cout << hash.Find(32) << endl;
	cout << hash.Find(42) << endl;
	cout << hash.Find(52) << endl;

	cout << endl;
	hash.Erase(32);
	cout << hash.Find(12) << endl;
	cout << hash.Find(22) << endl;
	cout << hash.Find(32) << endl;
	cout << hash.Find(42) << endl;
	cout << hash.Find(52) << endl;
}

int main()
{
	TestCloseHash();
	TestOpenHash();

	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

thinkadmin的form.html表单例子

<style>textarea {width: 100%;height: 200px;padding: 10px;border: 1px solid #ccc

JUC并发工具类的应用场景详解

目录 常用并发同步工具类的真实应用场景 1. ReentrantLock 1.1 常用API 1.2 ReentrantLock使用 独占锁&#xff1a;模拟抢票场景 公平锁和非公平锁 可重入锁 结合Condition实现生产者消费者模式 1.3 应用场景总结 2. Semaphore 2.1 常用API 2.2 Semaphore使…

VMware无法检测到插入的USB设备,虚拟机插拔USB无反应

原本正常使用的VMware虚拟机&#xff0c;在进行了重装软件后&#xff0c;发现虚拟机插拔USB设备都无法检测到&#xff0c;没有任何的反应和提示。 通过一系列的操作发现&#xff0c;在新安装了VMware workstation 软件后&#xff0c;存在一定的概率性会发生VMware虚拟机无法自…

在windows下安装docker部署环境运行项目----docker-compose.yml

前言 小编我将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识&#xff0c;有兴趣的小伙伴可以关注一下&#xff01; 也许一个人独行&#xff0c;可以走的很快&#xff0c;但是一群人结伴而行&#xff0c;才能走的更远&#xff01;让我们在成长的道路上互相学习&…

Android 13.0 原生SystemUI下拉通知栏每条通知默认展开

1.前言 在13.0的系统rom原生开发中,在在对SystemUI下拉通知栏做定制的时候,在下拉状态栏的时候,通知栏中最后一条通知默认是收缩的 点击按钮 就会展开 原生系统systemui就是如此,为了更美观 所以要求最后一条通知也默认展开,显得更美观 最终效果图: 2.原生SystemUI下拉通…

09. BI - 数据可视化,如何进行基本图形绘制

本文为 「茶桁的 AI 秘籍 - BI 篇 第 09 篇」 文章目录 EDA 作用可视化视图Python 进行可视化subplot Hi&#xff0c;你好。我是茶桁。 今天想给大家讲的是关于数据的可视化。在工作中很多时候我们不光要计算结果&#xff0c;还要把结果呈现出来&#xff0c;最好是一种图形化的…

斗地主登录界面(JAVA图形化界面)设置

1.实现代码 import CodeUtil.CodeUtil; import domain.User;import javax.swing.*; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.ArrayList;public class LoginGame extends JFrame implements MouseListen…

png图片怎么转换成jpg?四个方法搞定不求人

在数字图像处理领域&#xff0c;PNG和JPG是两种常见的图片格式。PNG以无损压缩而闻名&#xff0c;适用于保存透明背景和保留图像细节&#xff1b;而JPG以有损压缩而著称&#xff0c;适用于在较小的文件大小下保持照片质量。有时候&#xff0c;您可能需要将PNG格式的图片转换为J…

Win10系统搭建个人hMailServer邮件服务结合内网穿透远程发邮件

文章目录 前言1. 安装hMailServer2. 设置hMailServer3. 客户端安装添加账号4. 测试发送邮件5. 安装cpolar6. 创建公网地址7. 测试远程发送邮件8. 固定连接公网地址9. 测试固定远程地址发送邮件 前言 hMailServer 是一个邮件服务器,通过它我们可以搭建自己的邮件服务,通过cpola…

【Linux】统信服务器操作系统V20 1060a-AMD64 Vmware安装

目录 ​编辑 一、概述 1.1 简介 1.2 产品特性 1.3 镜像下载 二、虚拟机安装 一、概述 1.1 简介 官网&#xff1a;统信软件 – 打造操作系统创新生态 统信服务器操作系统V20是统信操作系统&#xff08;UOS&#xff09;产品家族中面向服务器端运行环境的&#xff0c;是一款…

【数据结构】链表OJ面试题2(题库+解析)

1.前言 前五题在这http://t.csdnimg.cn/UeggB 休息一天&#xff0c;今天继续刷题&#xff01; 2.OJ题目训练 1. 编写代码&#xff0c;以给定值x为基准将链表分割成两部分&#xff0c;所有小于x的结点排在大于或等于x的结点之前 。链表分割_牛客题霸_牛客网 思路 既然涉及…

6-树-二叉树的层序遍历 II

这是树的第7篇算法&#xff0c;力扣链接。 给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0c;逐层从左向右遍历&#xff09; 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,nu…

开源浏览器Firefox:使用Docker本地部署并远程访问进行测试

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 部署Firefox二. 本地访问Firefox三. Linux安装Cpolar四. 配置Firefox公网地址…

单片机学习笔记---定时器/计数器(简述版!)

目录 定时器的介绍 定时计数器的定时原理 定时计数器的内部结构 两种控制寄存器 &#xff08;1&#xff09;工作方式寄存器TMOD &#xff08;2&#xff09;控制寄存器TCON 定时计数器的工作方式 方式0 方式1 方式2 方式3 定时器的配置步骤 第一步&#xff0c;对…

YOLO部署实战(3):Darknet训练模型权重

1 一些概念和问题 YOLO中的darknet到底指的是什么&#xff1f; darknet到底是一个类似于TensorFlow、PyTorch的框架&#xff0c;还是一个类似于AlexNet、VGG的模型&#xff1f; 其实都是。YOLO作者自己写的一个深度学习框架叫darknet&#xff08;见YOLO原文2.2部分&#xff…

Leetcode2857. 统计距离为 k 的点对

Every day a Leetcode 题目来源&#xff1a;2857. 统计距离为 k 的点对 解法1&#xff1a;暴力 暴力枚举数组 coordinates 的两个点 coordinates[i] (x1, y1) 和 coordinates[j] (x2, y2)&#xff0c;它们的距离 d (x1 XOR x2) (y1 XOR y2) &#xff0c;XOR 指的是按位…

「 CISSP学习笔记 」08. 安全运营

该知识领域涉及如下考点&#xff0c;具体内容分布于如下各个子章节&#xff1a; 理解并遵守调查执行记录和监控活动执行配置管理 (CM)&#xff08;例如&#xff0c;预配、基线、自动化&#xff09;应用基本的安全操作概念应用资源保护执行事故管理执行和维护检测和预防措施实施…

QSlider使用笔记

最近做项目使用到QSlider滑动条控件&#xff0c;在使用过的过程中&#xff0c;发现一个问题就是点滑动条上的一个位置&#xff0c;滑块并没有移动到鼠标点击的位置&#xff0c;体验感很差&#xff0c;于是研究了下&#xff0c;让鼠标点击后滑块移动到鼠标点击的位置。 1、event…

【Linux】Ext2 文件系统

文件系统 前言一、磁盘硬件1. 磁盘的物理存储结构2. 磁盘存储的逻辑抽象结构 二、理解 Ext2 文件系统1. 初步理解文件系统2. 深入理解文件系统&#xff08;1&#xff09;inode Table&#xff08;2&#xff09;Data blocks&#xff08;3&#xff09;inode Bitmap&#xff08;4&a…

内衣洗衣机是不是鸡肋?好用的小型洗衣机全自动推荐

随着大家工作的压力越来越大&#xff0c;下了班之后只能想躺平&#xff0c;在洗完澡之后看着还需要手洗的内衣裤真的很头疼。有些小伙伴还有会攒几天再丢进去洗衣机里面一起&#xff0c;而且这样子是非常不好的&#xff0c;用过的内衣裤长时间不清洗容易滋生细菌&#xff0c;而…