先提一个简单的问题,假如有一个庞大的字符串数组,然后给你一个单独的字符串,让你从这个数组中查找是否有这个字符串并找到它,你会怎么做? 有一个方法最简单,老老实实从头查到尾,一个一个比较,直到找到为止,我想只要学过程序设计的人都能把这样一个程序作出来,但要是有程序员把这样的程序交给用户,我只能用无语来评价,或许它真的能工作,但...也只能如此了。 最合适的算法自然是使用HashTable(哈希表),先介绍介绍其中的基本知识,所谓Hash,一般是一个整数,通过某种算法,可以把一个字符串"压缩" 成一个整数,这个数称为Hash,当然,无论如何,一个32位整数是无法对应回一个字符串的,但在程序中,两个字符串计算出的Hash值相等的可能非常小,下面看看在MPQ中的Hash算法:
unsigned long HashString(char*lpszFileName, unsigned long dwHashType)
{
unsigned char*key = (unsigned char*)lpszFileName;
unsigned long seed1 =0x7FED7FED, seed2 =0xEEEEEEEE;
int ch;
while(*key !=0)
{
ch = toupper(*key );
seed1 = cryptTable[(dwHashType <<8) ch] ^ (seed1 seed2);
seed2 = ch+ seed1+ seed2 +(seed2 <<5) 3;
}
return seed1;
}
int GetHashTablePos(char*lpszString, SOMESTRUCTURE *lpTable, int nTableSize)
{
int nHash = HashString(lpszString), nHashPos = nHash % nTableSize;
if (lpTable[nHashPos].bExists &&!strcmp(lpTable[nHashPos].pString, lpszString))
return nHashPos;
else
return-1; //Error value
}
int GetHashTablePos(char*lpszString, MPQHASHTABLE *lpTable, int nTableSize)
{
constint HASH_OFFSET =0, HASH_A =1, HASH_B =2;
int nHash = HashString(lpszString, HASH_OFFSET);
int nHashA = HashString(lpszString, HASH_A);
int nHashB = HashString(lpszString, HASH_B);
int nHashStart = nHash % nTableSize, nHashPos = nHashStart;
while (lpTable[nHashPos].bExists)
{
if (lpTable[nHashPos].nHashA == nHashA && lpTable[nHashPos].nHashB == nHashB)
return nHashPos;
else
nHashPos = (nHashPos 1) % nTableSize;
if (nHashPos == nHashStart)
break;
}
return-1; //Error value
}
下面用一个静态数组做一个简单模拟(没有处理hash冲突):
#include
#define HASH_TABLE_SIZE 13 // 哈希表的大小应是个质数
struct mapping
{
void *key;
void *data;
} hash_table[HASH_TABLE_SIZE];
unsigned int
RSHash (char *str)
{
unsigned int b = 378551;
unsigned int a = 63689;
unsigned int hash = 0 ;
while (*str)
{
hash = hash * a + (*str++);
a *= b;
}
return (hash & 0x7FFFFFFF);
}
int main ()
{
char *str = "we are the world!";
char *filename = "myfile.txt";
unsigned int hash_offset;
// 初始化哈希表
memset (hash_table, 0x0, sizeof (hash_table));
// 将字符串插入哈希表 .
hash_offset = RSHash (str) % HASH_TABLE_SIZE;
hash_table[hash_offset].key = str;
hash_table[hash_offset].data = filename;
// 查找 str 是否存在于 hash_table.
hash_offset = RSHash (str) % HASH_TABLE_SIZE;
if (hash_table[hash_offset].key)
printf ("string '%s' exists in the file %s./n", str, hash_table[hash_offset].data);
else
printf ("string '%s' does not exist./n", str);
return 0;
}
代码
一、类声明头文件
/////////////////////////////////////////////////////////////////////////////
// Name: HashAlgo.h
// Purpose: 使用魔兽Hash算法,实现索引表的填充和查找功能。
// Author: 陈相礼
// Modified by:
// Created: 07/30/09
// RCS-ID: $Id: treetest.h 43021 2009-07-30 16:36:51Z VZ $
// Copyright: (C) Copyright 2009, TSong Corporation, All Rights Reserved.
// Licence:
/////////////////////////////////////////////////////////////////////////////
#define MAXFILENAME 255 // 最大文件名长度
#define MAXTABLELEN 1024 // 默认哈希索引表大小
//////////////////////////////////////////////////////////////////////////
// 测试宏定义,正式使用时关闭
#define DEBUGTEST 1
//////////////////////////////////////////////////////////////////////////
// 哈希索引表定义
typedef struct
{
long nHashA;
long nHashB;
bool bExists;
char test_filename[MAXFILENAME];
// ......
} MPQHASHTABLE;
//////////////////////////////////////////////////////////////////////////
// 对哈希索引表的算法进行封装
class CHashAlgo
{
public:
#if DEBUGTEST
long testid; // 测试之用
#endif
CHashAlgo( constlong nTableLength = MAXTABLELEN )// 创建指定大小的哈希索引表,不带参数的构造函数创建默认大小的哈希索引表
{
prepareCryptTable();
m_tablelength = nTableLength;
m_HashIndexTable =new MPQHASHTABLE[nTableLength];
for ( int i =0; i < nTableLength; i++ )
{
m_HashIndexTable[i].nHashA =-1;
m_HashIndexTable[i].nHashB =-1;
m_HashIndexTable[i].bExists =false;
m_HashIndexTable[i].test_filename[0] ='\0';
}
}
void prepareCryptTable(); // 对哈希索引表预处理
unsigned long HashString(char*lpszFileName, unsigned long dwHashType); // 求取哈希值
long GetHashTablePos( char*lpszString ); // 得到在定长表中的位置
bool SetHashTable( char*lpszString ); // 将字符串散列到哈希表中
unsigned long GetTableLength(void);
void SetTableLength( const unsigned long nLength );
~CHashAlgo()
{
if ( NULL != m_HashIndexTable )
{
delete []m_HashIndexTable;
m_HashIndexTable = NULL;
m_tablelength =0;
}
}
protected:
private:
unsigned long cryptTable[0x500];
unsigned long m_tablelength; // 哈希索引表长度
MPQHASHTABLE *m_HashIndexTable;
};
二、类实现文件
view plaincopy to clipboardprint?
/////////////////////////////////////////////////////////////////////////////
// Name: HashAlgo.cpp
// Purpose: 使用魔兽Hash算法,实现索引表的填充和查找功能。
// Author: 陈相礼
// Modified by:
// Created: 07/30/09
// RCS-ID: $Id: treetest.h 43021 2009-07-30 16:36:51Z VZ $
// Copyright: (C) Copyright 2009, TSong Corporation, All Rights Reserved.
// Licence:
/////////////////////////////////////////////////////////////////////////////
#include "windows.h"
#include "HashAlgo.h"
//////////////////////////////////////////////////////////////////////////
// 预处理
void CHashAlgo::prepareCryptTable()
{
unsigned long seed =0x00100001, index1 =0, index2 =0, i;
for( index1 =0; index1 <0x100; index1++ )
{
for( index2 = index1, i =0; i <5; i++, index2 +=0x100 )
{
unsigned long temp1, temp2;
seed = (seed *125+3) %0x2AAAAB;
temp1 = (seed &0xFFFF) <<0x10;
seed = (seed *125+3) %0x2AAAAB;
temp2 = (seed &0xFFFF);
cryptTable[index2] = ( temp1 | temp2 );
}
}
}
//////////////////////////////////////////////////////////////////////////
// 求取哈希值
unsigned long CHashAlgo::HashString(char*lpszFileName, unsigned long dwHashType)
{
unsigned char*key = (unsigned char*)lpszFileName;
unsigned long seed1 =0x7FED7FED, seed2 =0xEEEEEEEE;
int ch;
while(*key !=0)
{
ch = toupper(*key++);
seed1 = cryptTable[(dwHashType <<8) + ch] ^ (seed1 + seed2);
seed2 = ch + seed1 + seed2 + (seed2 <<5) +3;
}
return seed1;
}
//////////////////////////////////////////////////////////////////////////
// 得到在定长表中的位置
long CHashAlgo::GetHashTablePos(char*lpszString)
{
const unsigned long HASH_OFFSET =0, HASH_A =1, HASH_B =2;
unsigned long nHash = HashString(lpszString, HASH_OFFSET);
unsigned long nHashA = HashString(lpszString, HASH_A);
unsigned long nHashB = HashString(lpszString, HASH_B);
unsigned long nHashStart = nHash % m_tablelength,
nHashPos = nHashStart;
while ( m_HashIndexTable[nHashPos].bExists)
{
if (m_HashIndexTable[nHashPos].nHashA == nHashA && m_HashIndexTable[nHashPos].nHashB == nHash)
return nHashPos;
else
nHashPos = (nHashPos +1) % m_tablelength;
if (nHashPos == nHashStart)
break;
}
return-1; //没有找到
}
//////////////////////////////////////////////////////////////////////////
// 通过传入字符串,将相应的表项散列到索引表相应位置中去
bool CHashAlgo::SetHashTable( char*lpszString )
{
const unsigned long HASH_OFFSET =0, HASH_A =1, HASH_B =2;
unsigned long nHash = HashString(lpszString, HASH_OFFSET);
unsigned long nHashA = HashString(lpszString, HASH_A);
unsigned long nHashB = HashString(lpszString, HASH_B);
unsigned long nHashStart = nHash % m_tablelength,
nHashPos = nHashStart;
while ( m_HashIndexTable[nHashPos].bExists)
{
nHashPos = (nHashPos +1) % m_tablelength;
if (nHashPos == nHashStart)
{
#if DEBUGTEST
testid =-1;
#endif
returnfalse;
}
}
m_HashIndexTable[nHashPos].bExists =true;
m_HashIndexTable[nHashPos].nHashA = nHashA;
m_HashIndexTable[nHashPos].nHashB = nHash;
strcpy( m_HashIndexTable[nHashPos].test_filename, lpszString );
#if DEBUGTEST
testid = nHashPos;
#endif
returntrue;
}
//////////////////////////////////////////////////////////////////////////
// 取得哈希索引表长
unsigned long CHashAlgo::GetTableLength(void)
{
return m_tablelength;
}
//////////////////////////////////////////////////////////////////////////
// 设置哈希索引表长
void CHashAlgo::SetTableLength( const unsigned long nLength )
{
m_tablelength = nLength;
return;
}
三、测试主文件
view plaincopy to clipboardprint?
/////////////////////////////////////////////////////////////////////////////
// Name: DebugMain.cpp
// Purpose: 测试Hash算法封装的类,完成索引表的填充和查找功能的测试。
// Author: 陈相礼
// Modified by:
// Created: 07/30/09
// RCS-ID: $Id: treetest.h 43021 2009-07-30 16:36:51Z VZ $
// Copyright: (C) Copyright 2009, TSong Corporation, All Rights Reserved.
// Licence:
/////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// 测试参数设定宏
#define TESTNUM 32
#include
#include
#include "HashAlgo.h"
usingnamespace std;
//////////////////////////////////////////////////////////////////////////
// 测试主函数开始
int main( int argc, char**argv )
{
CHashAlgo hash_test( TESTNUM );
cout <<"取得初始化散列索引表长为:"<< hash_test.GetTableLength() << endl;
bool is_success = hash_test.SetHashTable( "test" );
if ( is_success )
{
cout <<"散列结果一:成功!"<< endl;
}
else
{
cout <<"散列结果一:失败!"<< endl;
}
is_success = hash_test.SetHashTable( "测试" );
if ( is_success )
{
cout <<"散列结果二:成功!"<< endl;
}
else
{
cout <<"散列结果二:失败!"<< endl;
}
long pos = hash_test.GetHashTablePos( "test" );
cout <<"查找测试字符串:\"test\" 的散列位置:"<< pos << endl;
pos = hash_test.GetHashTablePos( "测试" );
cout <<"查找测试字符串:“测试” 的散列位置:"<< pos << endl;
//////////////////////////////////////////////////////////////////////////
// 散列测试
for ( int i =0; i < TESTNUM; i++ )
{
char buff[32];
sprintf(buff, "abcdefg%d.", i);
is_success = hash_test.SetHashTable(buff);
is_success ? cout << buff <<"散列结果:成功!位置:"<< hash_test.testid << endl : cout << buff <<"散列结果:失败!"<< endl;
}
system( "pause" );
//////////////////////////////////////////////////////////////////////////
// 查找测试
for ( int i =0; i < TESTNUM; i++ )
{
char buff[32];
sprintf(buff, "abcdefg%d.", i);
pos = hash_test.GetHashTablePos( buff );
pos !=-1? cout <<"查找测试字符串:"<< buff <<" 的散列位置:"<< pos << endl : cout << buff <<"存在冲突!"<< endl;
}
system( "pause" );
return0;
}