哈希表(散列表)简单实现

来自百度

若结构中存在关键字和K相等的记录,则必定存储在f(K)的位置上。由此,不需比较便可直接取得所查记录。这个对应关系f称为 散列函数(Hash function),按这个思想建立的表为 散列表
* 对不同的关键字可能得到同一散列地址,即key1≠key2,而f(key1)=f(key2),这种现象称冲突。具有相同函数值的关键字对该 散列函数来说称做同义词。
* 综上所述,根据 散列函数H(key)和处理冲突的方法将一组 关键字映象到一个有限的连续的地址集(区间)上,并以关键字在地址集中的“象”, 作为这条记录在表中的存储位置,这种表便称为 散列表,这一映象过程称为散列造表或散列,所得的存储位置称散列地址。这个现象也叫散列桶,在散列桶中,只能通过顺序的方式来查找,一般只需要查找三次就可以找到。科学家计算过,当负载因子(load factor)不超过75%,查找效率最高。
* 若对于 关键字集合中的任一个关键字,经 散列函数映象到地址集合中任何一个地址的概率是相等的,则称此类散列函数为均匀散列函数(Uniform Hash function),这就是使关键字经过散列函数得到一个“随机的地址”,从而减少冲突。
-----------------------------

其实说白了,哈希表就是希望利用地址计算的方法来直接寻址,找到我们所需的数据存储位置,可是我们的数据都是不定的,例如字符串,结构体或者类对象,这些都不能像整数,自身就可以用来作为寻址index。

但是我们可以通过一定的算法将数据转换程我们需要的Key,然后利用这个key在我们的数组中寻址


举个简单的例子:假设我们现在需要制作一个字符串的存储,希望能够快速查找!O(1)最理想

1、那么对于字符串我们先将其转换成key,这个key和我们的value对应,(但是这里不是一一对应,所以存在冲突现象,后面解释怎么解决,方法很多)

我们假设所有字符的ascii值相加作为key,但是这个值很大,我们将其规范到0-49,也就是

while(key >= 50)
    key = key % 50;

下面是具体getKey函数

int getKey(char* value){
		int key = 0;
		int len = strlen(value);
		while(len>0){
			key += (*value);
			len--;
			value++;
		}
		while(key >= 50)
			key = key % 50;

		return key;
	}


有了这个key,那事情就变得简单多了,接下来就是给hashTable[key]赋值啦,不过不是简单的hashTable[key]  = value;

2、前面说过,可能存在不同value同一个key,所以需要解决冲突

我这里采用简单的线性解决办法:

其他参见:

--------------------------------------------------------

线性探查
      线性探查的思想是按照顺序的方式依次探查下一个位置,直到最大,然后用求余的方法回到哈希表的头部,继续探查到第一次探查的前一个位置,例如第一次找到的位置为h(k),下一次就是h(k)+1,…,m-1,0,1,…,h(k)-1,第一次探查的位置决定了整个探查序列。这是一个很容易实现的方法,但是存在一个问题:一次群集。随着哈希表逐渐被填满越来越容易出现群集的现象,连续被占用的序列会越来越长,导致查找时间越来越大。
 
      二次探查
      二次探查可以减轻群集带来的效率降低,它在每一次冲突之后为探查号i加上一个偏移,得到新的探查号,这个偏移量是i的二次函数,这种方法和线性探查的思路类似,效果要好得多,但是仍然会产生群集,和线性探查一样第一次探查的位置决定了整个探查序列。
 
      双重散列
      双重散列是开放寻址法最好的一种探查方式,简单的说就是每次产生碰撞,都会根据关键字用另一个哈希函数重新算一个哈希值,这种散列方法的散列函数形式为:
      h(k,i) = (h1(k) + i*h2(k)) mod m
      由于两次都重新使用不同的哈希函数进行计算,使得这种方法和理想的一致散列性能比较接近了。
--------------------------------------------------------


bool addValue(char* value){
		int key_origin = getKey(value);
		int key = key_origin;

		while(hashTable[key]!=0){
			key = (key+1) % MAX;
			if(key == key_origin){
				cout<<"the hash table is full!!"<<endl;
				return false;
			}
		}

		hashTable[key] = value;
		return true;
	}

基本上我们的表就建立好了

查找和删除其实就是基于addValue()改写即可,下面给完整代码哈

//============================================================================
// Name        : HashMap.cpp
// Author      : YLF
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;

class HashMap{
public:
	const static int MAX = 50;
	void init(){
		memset(hashTable,0,sizeof(hashTable));
	}
	bool addValue(char* value){
		int key_origin = getKey(value);
		int key = key_origin;

		while(hashTable[key]!=0){
			key = (key+1) % MAX;
			if(key == key_origin){
				cout<<"the hash table is full!!"<<endl;
				return false;
			}
		}

		hashTable[key] = value;
		return true;
	}

	void delValue(char* value){
		;
	}
	bool exist(char* value){
		int key_origin = getKey(value);
		int key = key_origin;

		while(hashTable[key]!=0){
			if(0 == strcmp(value, hashTable[key]))
				return true;
			key = (key+1) % MAX;
			if(key == key_origin)
				return false;
		}
		return false;
	}

	void printTable(){
		int i=0;
		for(;i<50;i++){
			if(hashTable[i]!=0)
				cout<<i<<":"<<hashTable[i]<<endl;
			else
				cout<<i<<":"<<"no data"<<endl;
		}
	}

private:
	char* hashTable[MAX];

	int getKey(char* value){
		int key = 0;
		int len = strlen(value);
		while(len>0){
			key += (*value);
			len--;
			value++;
		}
		while(key >= 50)
			key = key % 50;

		return key;
	}
};

int main() {
	HashMap* hm = new HashMap();
	char* s1 = "ylf";
	char* s2 = "dsb";
	char* s3 = "zjq";
	char* s4 = "sdf";
	char* s5 = "ase";

	//init hash table
	hm->init();
	//add value
	hm->addValue(s1);
	hm->addValue(s2);
	hm->addValue(s3);
	hm->addValue(s4);
	hm->addValue(s5);
	//print table
	hm->printTable();
	//search value
	if(hm->exist("ase"))
		cout<<"find "<<endl;
	else
		cout<<"not find"<<endl;
	return 0;
}


输出

0:no data
1:no data
2:no data
3:no data
4:no data
5:no data
6:no data
7:no data
8:no data
9:no data
10:no data
11:no data
12:no data
13:dsb
14:ase
15:no data
16:no data
17:sdf
18:no data
19:no data
20:no data
21:no data
22:no data
23:no data
24:no data
25:no data
26:no data
27:no data
28:no data
29:no data
30:no data
31:ylf
32:no data
33:no data
34:no data
35:no data
36:no data
37:no data
38:no data
39:no data
40:no data
41:zjq
42:no data
43:no data
44:no data
45:no data
46:no data
47:no data
48:no data
49:no data
find 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值