咱们一直在讲哈希,哈希,可是真正用这个数据结构的时候每每采用的是它的简化形式ios
那么如何构造一个真正的哈希表呢?数组
首先咱们明确一下哈希表是干啥用的,没错就是用来判重和查找的数据结构
可是这个判重,咱们要规定一下限制范围, 虽然哈希表功能强大可是仍是有局限性函数
哈希表适合那种数据特别多,可是对于每个数据,它的值不是很大的状况spa
在使用哈希表以前,咱们先来进行一个科普,C++的int转string与string转intcode
在这里最好不用调用什么函数,你不懂它blog
string转int istringstream is("12"); //构造输入字符串流,流的内容初始化为“12”的字符串 int i; is >> i; //从is流中读入一个int整数存入i中 int转string ostringstream os; //构造一个输出字符串流,流内容为空 int i = 12; os << i; //向输出字符串流中输出int整数i的内容 cout << os.str() << endl; //利用字符串流的str函数获取流中的内容
首先咱们给出哈希表的一些定义内存
const int max_hash_size=1000005; const int maxn=1005; int head[max_hash_size]; //存数据的数组 int Next[maxn]; //记录同地址元素的下一个元素的值 int st[maxn]; //数据集
为啥这个max_hash_size很大,由于这个范围是每个元素最大是多少而不是一共有多少个元素,哈希表就是用来解决元素很是多的那种状况的字符串
若是单纯使用bool数组的话受制于内存的限制,查找能力实在有限,哈希表在本质上就是拓宽了这个bool数组的范围string
这里的head和Next就是链表,而后咱们给出哈希函数
int Hash(int& s) { return(s*2654435769)>>28; } int HashingDouble(double d) { if (d==0) return 0; else { int exponent; double mantissa = frexp(d, &exponent); return (unsigned int)((2*mantissa-1) * (~0U)); } } int HashingString(char *str, int iLen) { int hsval = 2654435769; int i; int iShift = 0; for(i=0; i<iLen; i++) { hsval ^= (str[i]<<iShift); iShift+=3; if(iShift>24) iShift=0; } return hsval; }
你能够把哈希函数理解为根据元素的值来计算这个元素下标的函数,而后咱们回头去看那个max_hash_size,而后理解一下:
若是咱们不用哈希函数,那么数据有多少,max_hash_size就是多少,若是咱们用了哈希函数,就把原数据范围进行了一个放缩,放缩到了空间所容许的一个范围内
这个时候咱们再取理解maxn是啥,既然max_hash_size是通过放缩以后的数据有多少个,那么maxn就是每个数据的数据范围了
因为哈希碰撞的缘由,同哈希值元素可能有多个,咱们在链表查找到地址的时候,要顺着链表一个一个找看是否是
下面给出尝试插入哈希表的函数,若是不重复就能够插进去,不然就插不进去返回0
int try_to_insert(int s) { int h=Hash(st[s]); //获得s元素的数组下标 int u=head[h]; //获得这个下标下的元素值 while(u) //顺着链表一个一个找 { if(st[u]==st[s]) //若是在链表中找到了s,说明这个s已经访问过了 return 0; u=Next[u]; //此时u是下一个在此位置的元素值 } Next[s]=head[h]; //头插法 head[h]=s; return 1; }
由于实现的过程当中有一个
int u=head[h];
while(u)
这么一个操做,这应该是这种方法实现哈希表的一个缺陷,因此在插入的时候,千万保证非0
下面给出哈希表完整的实现代码,若是有更好的实现方法,会在本篇文章的基础上迭代
1 //适用于数据特别多可是每一个数的范围不是很大的状况下查找 2 /* 3 string转int 4 istringstream is("12"); //构造输入字符串流,流的内容初始化为“12”的字符串 5 int i; 6 is >> i; //从is流中读入一个int整数存入i中 7 int转string 8 ostringstream os; //构造一个输出字符串流,流内容为空 9 int i = 12; 10 os << i; //向输出字符串流中输出int整数i的内容 11 cout << os.str() << endl; //利用字符串流的str函数获取流中的内容 12 */ 13 #include<iostream> 14 #include<cstring> 15 #include<cmath> 16 using namespace std; 17 const int max_hash_size=1000005; 18 const int maxn=1005; 19 int head[max_hash_size]; //存数据的数组 20 int Next[maxn]; //记录同地址元素的下一个元素的值 21 int st[maxn]; //数据集 22 void init_lookup_table() 23 { 24 memset(head,0,sizeof(head)); 25 } 26 int Hash(int& s) 27 { 28 return(s*2654435769)>>28; 29 } 30 int HashingDouble(double d) 31 { 32 if (d==0) 33 return 0; 34 else 35 { 36 int exponent; 37 double mantissa = frexp(d, &exponent); 38 return (unsigned int)((2*mantissa-1) * (~0U)); 39 } 40 } 41 int HashingString(char *str, int iLen) 42 { 43 int hsval = 2654435769; 44 int i; 45 int iShift = 0; 46 for(i=0; i<iLen; i++) 47 { 48 hsval ^= (str[i]<<iShift); 49 iShift+=3; 50 if(iShift>24) 51 iShift=0; 52 } 53 return hsval; 54 } 55 int try_to_insert(int s) 56 { 57 int h=Hash(st[s]); //获得s元素的数组下标 58 int u=head[h]; //获得这个下标下的元素值 59 while(u) //顺着链表一个一个找 60 { 61 if(st[u]==st[s]) //若是在链表中找到了s,说明这个s已经访问过了 62 return 0; 63 u=Next[u]; //此时u是下一个在此位置的元素值 64 } 65 Next[s]=head[h]; //头插法 66 head[h]=s; 67 return 1; 68 } 69 int main() 70 { 71 //必须保证数据集中,查找的位置有值 72 for(int i=1;i<=10;i++) 73 st[i]=i-1; 74 init_lookup_table(); 75 for(int i=1;i<=5;i++) 76 { 77 if(try_to_insert(i)) 78 cout<<"Yes"<<endl; 79 else 80 cout<<"No"<<endl; 81 } 82 cout<<endl; 83 for(int i=3;i<=7;i++) 84 { 85 if(try_to_insert(i)) 86 cout<<"Yes"<<endl; 87 else 88 cout<<"No"<<endl; 89 } 90 return 0; 91 }