Hash表(哈希表) (hash法(哈希法),散列表,散列法)
通过一个例子了解hash法
它具备数据结构特性也具备算法特性
查找效率非常高
绝大多数编程语言都是用哈希表这种结构储存函数的代码
说一个梗:php是世界上最好的语言
说这个梗的原因是因为:php相比于其他语言编译器,它对函数存储的hash表是比较low的,或者说php所用的用来存储函数的hash法不是很好。
Hash表的效率取绝于你设置的hash法,你设置的hash法越好,效率也会越好。
它同样也是算法,主要用于加密解密(非对称加密)。
不论数据是什么,它会固定生成一个的一个串(原则上讲不同数据生成的串是不一样的),为什么叫非对称加密,因为它不能将串反向生成数据(它是单向的)。可以用来做验证。
举例:客户端写入的密码变成串,串与服务器密码库内的串做验证,是不是一样,一样即可通过验证
最具代表性的是sha1(Secure Hash Algorithm,安全散列算法),md5(Message Digest algorithm 5,信息摘要算法)(被誉为最安全的加密方式)
详情搜索sha1和md5查找相关消息
简单学习一下(因为太nb和大了):
当创建hash法的方式就有如图这么多
还有处理冲突的方式等等,范围很广也很多
所以该文章只是简单写一下:
首先需要了解的是hash法的三要素
数据的范围
合适的hash函数 查找数据的方式
返回数据存放地址
解决冲突的方法。
不同数据通过第二步的hash函数返回地址相同 要区分存储
举例:打败游戏boss,在那一瞬间计算出会爆出啥东西,然后渲染到客户端,速度很快,问题是客户端这边要把信息发给服务端,然后服务端要很快计算完发给客户端
比如掉金币,戒指,武器等等,他们每一个里面同时也包含了各种各样的数据
需要的效率是非常高的
所以我们需要一个很短的方式去描写很大数据的方式,所以要对这些进行分类
这样,不论啥玩意,用7个位描述清楚
举例:4 7 80 001就是道具,红色,80级,剑(假设001是剑)这样,以此类推
然后在这些数里面做随机,就会很快,这就是数据的范围
存储十进制整数:
用10个位置的数组,每个位存指针,指向下一个数组
比如要找编号567
则:
注意,指向的是数组(这样就不会很大,并且只需要3次寻址即可)
1000以内不冲突,但1w以内就会产生冲突
会发生冲突的可能是567,1567,2567,3567等等都在这个位置
解决冲突可以用链表,数组等等
三层数组利用十进制数组
可以搜索md5源码,是用数字的映射制作的,非常复杂(中间加了很多步骤,为的是难以解密以及冲突很小)
所以用存储数字的方式写一个hash看一看:
c语言写的hash:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//解决冲突 用链表
struct Node
{
int data;
Node* pNext;
};
//hash表结构
//Node* arr[10][10][10];
//Node**** pArr = NULL;
struct HashTest
{
Node**** pArr;
};
//创建节点
Node* createNode(int data);
//初始化hash
void initHash(HashTest* hash);
//往hash表中放入数据
void push(HashTest* hash, int data);
//hash函数
Node* findDataFromHash(HashTest* hash, int data);
int main()
{
HashTest h;
initHash(&h);
//测试:
int n;
int data;
Node* pT;
while (1)
{
printf("1.插入\n");
printf("2.查找\n");
printf("3.退出\n");
scanf("%d", &n);
switch (n)
{
case 1:
printf("请输入插入数据:");
scanf("%d", &data);
push(&h, data);
printf("插入完毕\n");
break;
case 2:
printf("请输入查找数据:");
scanf("%d", &data);
pT = findDataFromHash(&h, data);
if (pT)
{
printf("找到了: %d!\n", pT->data);
}
else
{
printf("没找到\n");
}
break;
case 3:
return 0;
}
}
return 0;
}
Node* createNode(int data)
{
Node* pNew = (Node*)malloc(sizeof(Node));
pNew->data = data;
pNew->pNext = NULL;
return pNew;
}
void initHash(HashTest* hash)
{
hash->pArr = (Node****)malloc(sizeof(Node***) * 10);//第一层
for (int i = 0; i < 10; i++)
{
hash->pArr[i]= (Node***)malloc(sizeof(Node**) * 10);//第二层
for (int j = 0; j < 10; j++)
{
hash->pArr[i][j] = (Node**)malloc(sizeof(Node*) * 10);//第三层
memset(hash->pArr[i][j], 0, sizeof(Node*) * 10);//置空
}
}
}
void push(HashTest* hash, int data)
{
//1.创建节点
Node* pNew = createNode(data);
//2.寻址
int bai = data / 100 % 10;
int shi = data / 10 % 10;
int ge = data % 10;
Node* pTemp = hash->pArr[bai][shi][ge];
//3.解决冲突 这里用的是拉链法(链地址法)
if (pTemp)
{
//找到最后一个节点
while (pTemp->pNext)
{
pTemp = pTemp->pNext;
}
//新节点成为最后一个节点的下一个节点
pTemp->pNext = pNew;
}
else//本来为空,说明没有数据
{
hash->pArr[bai][shi][ge] = pNew;
}
}
Node* findDataFromHash(HashTest* hash, int data)
{
int bai = data / 100 % 10;
int shi = data / 10 % 10;
int ge = data % 10;
Node* pTemp = hash->pArr[bai][shi][ge];
while (pTemp)
{
if (data==pTemp->data)
{
return pTemp;
}
pTemp = pTemp->pNext;
}
return NULL;
}
c++写的hash:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<vector>
using namespace std;
//解决冲突 用链表
struct Node
{
int data;
Node* pNext;
};
//hash表结构
//Node* arr[10][10][10];
//Node**** pArr = NULL;
struct HashTest
{
vector<vector<vector<Node*>>> table;
};
//创建节点
Node* createNode(int data);
//初始化hash
void initHash(HashTest* hash);
void push(HashTest* hash, int data);
Node* findDataFromHash(HashTest* hash, int data);
int main()
{
HashTest h;
initHash(&h);
//测试
int n;
int data;
Node* pT;
while (1)
{
printf("1.插入\n");
printf("2.查找\n");
printf("3.退出\n");
scanf("%d", &n);
switch (n)
{
case 1:
printf("请输入插入数据:");
scanf("%d", &data);
push(&h, data);
printf("插入完毕\n");
break;
case 2:
printf("请输入查找数据:");
scanf("%d", &data);
pT = findDataFromHash(&h, data);
if (pT)
{
printf("找到了: %d!\n", pT->data);
}
else
{
printf("没找到\n");
}
break;
case 3:
//goto overSee;//用于断点查看
return 0;
}
}
overSee:
while (1){}
return 0;
}
Node* createNode(int data)
{
Node* pNew = new Node;
pNew->data = data;
pNew->pNext = NULL;
return pNew;
}
void initHash(HashTest* hash)
{
vector<vector<vector<Node*>>> table;
hash->table = table;
for (int i = 0; i < 10; i++)
{
vector<vector<Node*>> t;
hash->table.push_back(t);
for (int j = 0; j < 10; j++)
{
vector<Node*> temp;
for (int k = 0; k < 10; k++)
{
temp.push_back(NULL);
}
hash->table[i].push_back(temp);
}
}
}
void push(HashTest* hash, int data)
{
//1.创建节点
Node* pNew = createNode(data);
//2.寻址
int bai = data / 100 % 10;
int shi = data / 10 % 10;
int ge = data % 10;
Node* pTemp = hash->table[bai][shi][ge];
//3.解决冲突 这里用的是拉链法(链地址法)
if (pTemp)
{
//找到最后一个节点
while (pTemp->pNext)
{
pTemp = pTemp->pNext;
}
//新节点成为最后一个节点的下一个节点
pTemp->pNext = pNew;
}
else//本来为空,说明没有数据
{
hash->table[bai][shi][ge] = pNew;
}
}
Node* findDataFromHash(HashTest* hash, int data)
{
int bai = data / 100 % 10;
int shi = data / 10 % 10;
int ge = data % 10;
Node* pTemp = hash->table[bai][shi][ge];
while (pTemp)
{
if (data == pTemp->data)
{
return pTemp;
}
pTemp = pTemp->pNext;
}
return NULL;
}
在cpp的代码中可以在最后断点来看看:
c和c++的代码其实差不多,只不过是一些地方的区别。