您的位置:首页 > 其它

哈希——拉链法

2018-03-01 15:25 357 查看

首先写哈希——拉链法要知道哈希冲突。

    哈希冲突: 对于两个数据元素的关键字 Ki  和 Kj  (i != j),有 Ki != J ,但有:   HashFun(Ki) == HashFun(Kj)   即不同关键字通过相同哈希哈数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞。把具有不同关键码而具有相同哈希地 址的数据元素称为“同义词”

所以解决哈希冲突两种常见的方法是:闭散列和开散列

本文主要介绍开散列——拉链法:

        开散列法:首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个 桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。 

首先定义结构体:

typedef int Keytype;
typedef int Valtype;

typedef struct HashNode
{
HashNode *_next;
Keytype _key;
Valtype _val;
}HashNode;
typedef struct HashTable
{
HashNode **_table;
size_t _N;
size_t _size;
}HashTable;

函数定义:

HashNode *HashBuyNode(Keytype key,Valtype val)//创建节点
{
HashNode *node=(HashNode*)malloc(sizeof(HashNode));
node->_key=key;
node->_val=val;
node->_next=NULL;

assert(node);
return node;
}
size_t HashFunc(Keytype key,size_t n)//这个函数主要作用就是判断要查找的数在哪个哈希桶里
{
return key%n;
}
size_t GetNextPrime(size_t cur)//获取素数N,进行扩容时用到的
{
const int _PrimeSize = 28;
static const unsigned long _PrimeList[_PrimeSize] =
{//素数表
53ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul,
786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul,
25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul,
805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
for (size_t i = 0; i < _PrimeSize; ++i)
{
if (_PrimeList[i] > cur)
{
return _PrimeList[i];
}

}
return _PrimeList[_PrimeSize - 1];
}
void HashTableInit(HashTable *ht)//初始化
{
assert(ht);
ht->_size=0;
ht->_N=53;
ht->_table=(HashNode**)malloc(sizeof(HashNode*)*ht->_N);
memset(ht->_table,NULL,sizeof(HashNode*)*ht->_N);//都置为空

}

哈希——拉链法插入:(扩容是重点部分,每次扩容后插入的数的坐标需重新计算)

int HashTableInsert(HashTable *ht,Keytype key,Valtype val)//拉链法的插入
{
//扩容需要重新开辟空间
if (ht->_size==ht->_N)//扩容
{
size_t newN=GetNextPrime(ht->_N);
HashNode **newTable=(HashNode**)malloc(sizeof(HashNode*)*newN);
memset(newTable,NULL,sizeof(HashNode*)*newN);
for (size_t i=0;i< ht->_N ;i++)
{
HashNode *cur=ht->_table[i];
while (cur)
{
//HashNode *next=cur->_next;
size_t Newindex=HashFunc(cur->_key,newN);
//头插
cur->_next=newTable[Newindex];
newTable[Newindex]=cur;
cur=cur->_next;
}
}
free(ht->_table);//释放原哈希表
ht->_table=newTable;
ht->_N=newN;
}

size_t index=HashFunc(key,ht->_N);//找出该数在哪个哈希表里

HashNode *cur=ht->_table[index];//判断是否包含
while (cur)
{
if (cur->_key==key)
{
return -1;
}
cur=cur->_next;
}
//进行插入 应用头插法
HashNode *node=HashBuyNode(key,val);
node->_next= ht->_table[index];
ht->_table[index]=node;
++ht->_size;
return 0;
}

哈希——拉链法的寻找与删除:

HashNode *HashTableFind(HashTable *ht,Keytype key)
{
size_t index=HashFunc(key,ht->_N);
HashNode*cur=ht->_table[index];
while(cur)
{
if (cur->_key==key)
{
return cur;
}
cur=cur->_next;
}
return NULL;
}
int HashTableRemove(HashTable *ht,Keytype key)//移除
{
size_t index=HashFunc(key,ht->_N);
HashNode *cur=ht->_table[index];
HashNode *prev=NULL;
while (cur)
{
if (cur->_key==key)
{
if (prev==NULL)
{
ht->_table[index]=cur->_next;
}
else
{
prev->_next=cur->_next;
}
free(cur);
cur=NULL;
--ht->_size;
return 0;
}
prev=cur;
cur=cur->_next;
}
return -1;

}

哈希——打印函数:

void HashPrint(HashTable *ht)//打印哈希表
{
for (size_t i=0;i< ht->_N ;i++)
{
HashNode *cur=ht->_table[i];
printf("[%d]",i);
while(cur)
{
printf("%d->",cur->_key);
cur=cur->_next;
}
if (cur==NULL)
{
printf("NULL\n");
}
}
}

测试用例:

int main()
{
HashTable ht;
HashTableInit(&ht);
HashTableInsert(&ht,1,2);
HashTableInsert(&ht,1,2);
HashTableInsert(&ht,2,2);
HashTableInsert(&ht,3,2);
HashTableInsert(&ht,54,2);
HashTableRemove(&ht,3);
HashPrint(&ht);

system("pause");
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息