现在的位置: 首页 > 综合 > 正文

hash海量数据查询的一个实现 :周末练手

2013年09月20日 ⁄ 综合 ⁄ 共 4510字 ⁄ 字号 评论关闭

       前言:闲来蛋疼,周末在家陪老婆,中午还要亲自操厨,操厨之前,加深下对hash的理解,写下此篇。

       正文:本篇文章主要模拟了海量搜索过程。从txt文件获取大量数据信息(模拟海量),建立hash表,然后输入关键字(字符串),能迅速定位要找的value。其实就是搜索某个特定的字符串。具体会贴出代码,和验证图。整个过程模拟了海量查找的过程,能切实感觉到hash 海量处理的优势。

     先贴几张运行效果图:

                                                                                                                        

                                                                                    图 1

        稍微解释一下:结合图1 中的第一行,  第359次,指发生映射的顺序数;992处,指哈希数组中的位置,即hashTable[992];存入的字符串,为我们哈希映射以后把原字符串存起来,以便查找时比较;value,指映射后要定位的值,比如:我们要查找字符串出现的概率,或是字符串出现的次数等等,在程序里相当于留了一个借口。注意:所有的数据从txt里读取,而txt里的的数据由程序模拟生成,采用rand()函数。具体见后面的程序源码或下载后面贴出的工程文件的下载地址。我生成的是5000个,通过修改宏参数,可以更多,非常贴近真实。

   

                                                                                      

                                                                           图 2

       图 2 最后一行,显示了要查找字符串的位置。如下图,蓝色部分。

  

 

      网上先搜索了一番,发现要么就是理论一堆,要么就是贴出关键代码,让我等菜鸟始终没法体会海量的涵义。

      本文章分为三大部分。第一部分:hash的基本知识,第二部分,hash映射的建立和冲突的解决,具体代码实现

       hash 的主要作用就是快速查找,其时间复杂度是O(1),是最好的查找算法。对于hash的入门级别可参考我的一篇其他博文 数据结构-练习2 哈希表。hash的最基本理解就是:根据要查找的字符串的ASCII(经过变换处理),通过哈希函数,转换成与其存储位置的下标,这样在hash表建立以后,我们就可以根据字符串本身,以相同的哈希函数映射方式迅速定位到其位置,然后查找出相关信息。
 哈希函数的构造就成为了最重要的任务之一,常见的方法有:直接取址法,平方取中法,除留余数法,数字分析法,折叠法,随机数法。

本文章的例子采用除留余数法:

                                                                              基本原理为:H(key)=key%MOD,MOD 为小于表长的一个数,本例子为1024.

结合代码具体分析(SDBM法):

                              

int SDBMHash(char* str)
    {
        int hash = 0;
 
       while(*str!='\0')
       { 
          //相当于:65599*hash+*str++,这就是映射函数,通过字符串里每个字符的ASCIIl累计形成
          hash = *str++ + (hash << 6) + (hash << 16) - hash;
       }
 
       return (hash & 0x7FFFFFFF);
    }

最后别忘了求余数:key=key%HashTableLen;//HashTableLen=1024,key就是SDBMHash的返回值。

冲突解决是第二个重要的过程,基本方法包括:链表法,开放地址法,再哈希,开辟公共溢出区。

本文章例子采用常见的链表法:

                                                                                                                              

如图,左边为:第一次获得的key的位置,显然,两个不同的字符串很可能有相同的key,这时我们在后面加个链表存储一下。

代码如:

                 

 p=&HashT[key];
	while(p->next!=NULL) p=p->next;
	{
		p->next=new hashNode();
		p->next->used=true;
		strcpy(p->next->key,str);		
		p->next->value=rand()%100;
        }

   贴完整个程序的代码:

             

#include<iostream>
#include<fstream>
#include<string>
using namespace std;
const unsigned int NUM=5000;
const int HashTableLen=1024;
int count=0;//用于记录hash映射时,发生的次数,验证作用
struct hashNode 
{
bool used;
hashNode* next;
char  key[28];
unsigned int value;
hashNode()
{
used=false;
next=NULL;

}

hashNode(char* KEY,unsigned int VALUE)
{
strcpy(key,KEY);
value=VALUE;
}

}HashT[HashTableLen] ;// 哈希表的长度为1024,所以取摸的时候用1024;

unsigned int elfhash(char* s);//declare the function
void createStrTXT();
void establishHaT();
void  FindStr(char*);
int main()
{

   
 //for(int i=0;i<50000;++i)
 //生成txt,文件,往里面随机写入字符串。
 
  //createStrTXT();
  establishHaT();
  

  FindStr("qd`e_usigh]oscfwsnfreshayug");
  
  //FindStr();
 /* char str[30];
  ifstream ifs("str.txt");
  ifs>>str;
  */
}


 int SDBMHash(char* str)
    {
        int hash = 0;
 
       while(*str!='\0')
       {
          hash = *str++ + (hash << 6) + (hash << 16) - hash;
       }
 
       return (hash & 0x7FFFFFFF);
    }


unsigned int elfhash(char *s){
    int hash = 0, x = 0;
    while (*s){
        hash = (hash << 4) + (*s++);
        if(((x = hash) & 0xf0000000L) != 0){
            hash ^= (x >> 24);
            hash &= ~x;
        }
    }
    return hash & 0x7fffffff;
}

void createStrTXT()
{
	for(int i=0;i<NUM;++i)
	{		
		char temp[30]={'\n','\r',rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91
		,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91,rand()%31+91
		,rand()%31+91,rand()%31+91,'\0'};
	 
        char*str=temp;
        ofstream ofs("str.txt",ios::app);
        ofs<<str;
	}
}

void  establishHaT()//采用链表法构造hash表

{
  hashNode * p;
  int key;
  char str[28];
  ifstream ifs("str.txt");

  while( ifs>>str)
  {
   key=SDBMHash(str);
   key=key%HashTableLen;
   if(HashT[key].used==false)
   {
   strcpy(HashT[key].key,str);
   HashT[key].used=true;
   HashT[key].value=rand()%100; 
   count++;                                                    //伪随机的目的是模拟key-value 里的value,里面就是我们要查找的值,e.g. 如果要查找字符串是否存                                                               //在,
   printf("第%d次在 %d处发生了映射,存入的字符串为:%s,value为:%d\n",count,key,str,HashT[key].value);//里面就村字符串本身;如果要查某字符串出现的概率,
                                                              //里面就是概率值,如果查找的是字符串出现的次数,里面存的就是次数,正如百度的那道查找top 10的                                                              //面试题,原理跟这一样
   }
   else//冲突处理
   {
    p=&HashT[key];
	while(p->next!=NULL) p=p->next;
	{
		p->next=new hashNode();
		p->next->used=true;
		strcpy(p->next->key,str);		
		p->next->value=rand()%100;
		count++;
        printf("第%d次在 %d处发生了映射,存入的字符串为:%s,value为:%d\n",count,key,str,HashT[key].value);  
        
	}
   
   
   }
  
  }
  
}

void  FindStr(char* str)
{
  
  hashNode * p;
  int key;
  
  key=SDBMHash(str);
  key=key%HashTableLen;
  if(HashT[key].used==false)
  {
    printf("此字符串不存在");                                                             
  }
  else
  {   
  p=&HashT[key];
  while(p!=NULL)
  {
	  if(!strcmp(p->key,str)) 
	  {
		  printf("在%d位置处找到了目的字符串:%s",key,str);
		  break;
	  }
	  else
	  {
		  p=p->next;
	  }
	  if(p==NULL) printf("SORRY!字符串没有匹配的");
  }
  }
  }
    
                          
说明:函数 FindStr(char*)主要是查找特定的字符串,原理与构造hash函数的过程一样。注意的是:写入函数参数中的字符串,不应该含有转义符,因为转义符号,程序可能不认识,所以,在TXT里选一个没有转移符号的作为参数,如本例。
createStrTXT();主要是创建txt文件,NUM是创建的字符串的个数,注意的是,我创建时,写入文件采用了ios::app,所以每次运行程序,如果都createStrTXT(),txt里的字符 串个数会每次都增加5000次。所以创建一次就可以,以后就注销。工程文件下载地址:此处下载。需要一积分,不好意思,我想攒点分下东西,没积分的 留下邮箱。我将速度发送。

                                                                                    




















































抱歉!评论已关闭.