搜索结构之哈希-----开散列

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sx2448826571/article/details/80351511
搜索结构之哈希-----开散列

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

设元素的关键码为37, 25, 14, 36, 49, 68, 57, 11, 散列表为HT[12],表的大小为12,散列函数为Hash(x) = x % 11
Hash(37)=4
Hash(25)=3
Hash(14)=3
Hash(36)=3
Hash(49)=5
Hash(68)=2
Hash(57)=2
Hash(11)=0
使用哈希函数计算出每个元素所在的桶号,同一个桶的链表中存放哈希冲突的元素。


通常,每个桶对应的链表结点都很少,将n个关键码通过某一个散列函数,存放到散列表中的m个桶中,那么每一个桶中链表的平均长度为n/m。以搜索平均长度为的链表代替了搜索长度为 n 的顺序表,搜索效率快的多。
应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。事实上:

由于开地址法必须保持大量的空闲空间以确保搜索效率,如二次探查法要求装载因子a <= 0.7,而表项所占空间又比指针大的多,所以使用链地址法反而比开地址法节省存储空间。

哈希表(开散列)

Hash_Bucket.h

typedef int DataType;
typedef struct Hash_Node{
	struct Hash_Node* _pNext;
	DataType _data;
}Hash_Node,*pHash_Node;
typedef int (*pElemToInt)(DataType data);
typedef struct Hash_Bucket{
	pHash_Node* _HashTable;
	int _size;
	int _capacity;
	pElemToInt _pElemToInt;
}Hash_Bucket;
int ElemToint(int data);
int Strtoint(const char * str);
int GetNextPrimeNum(int Old_Capacity);
//初始化
void  HashBucketInit(Hash_Bucket *hb, pElemToInt ElemToInt);

//扩容
void HashBucketAddCapacity(Hash_Bucket *hb);

//插入(元素不重复)
void  HashBucketInsertUnique(Hash_Bucket*hb, DataType data);

//插入(元素可以重复)
void  HashBucketInsertEqual(Hash_Bucket*hb, DataType data);

//删除
void  HashBucketDeleteUnique(Hash_Bucket*hb, DataType data);

//删除所有指定元素
void  HashBucketDeleteEquel(Hash_Bucket*hb, DataType data);

//找到指定元素
Hash_Node*  HashBucketFind(Hash_Bucket*hb, DataType data);

//桶的大小
int  HashBucketSize(Hash_Bucket*hb);

//桶是否为空
int HashBucketEmpty(Hash_Bucket*hb);

//销毁哈希桶
void HashBucketDestory(Hash_Bucket*hb);

//打印哈希桶
void HashBucketPrint(Hash_Bucket*hb);

Hash_Bucket.c

#include "HashBucket.h"
#include<malloc.h>
#include<stdlib.h>
#include<stdio.h>
#include<assert.h>
int HashFun(Hash_Bucket *hb,DataType data){
	assert(hb);
	return  (hb->_pElemToInt)(data) % (hb->_capacity);
}
//哈希字符串转换函数
int Strtoint(const char * str)
{
	unsigned int seed = 131; // 31 131 1313 13131 131313
	unsigned int hash = 0;
	while (*str)
	{
		hash = hash * seed + (*str++);
	}
	return (hash & 0x7FFFFFFF);
}
//默认处理方式
int ElemToint(int data){
	return data;
}
//素数表
int GetNextPrimeNum(int Old_Capacity){
	int i = 0;
	const int _PrimeSize = 28;
	static const unsigned long _PrimeList[28] =
	{
		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 (i = 0; i < _PrimeSize; i++)
	{
		if (_PrimeList[i]>Old_Capacity)
			return _PrimeList[i];
	}
	return -1;
}
//扩容
void HashBucketAddCapacity(Hash_Bucket *hb){
	pHash_Node pcur = NULL;
	int i = 0;
	assert(hb);
	int new_capacity = GetNextPrimeNum(hb->_capacity);
	pHash_Node * _newTable = (pHash_Node *)malloc(sizeof(pHash_Node)*new_capacity);
	if (_newTable==NULL)
	{
		assert(0);
		return;
	}
	for ( i = 0; i < new_capacity; i++)
	{
		_newTable[i]->_pNext = NULL;
	}
	for (i=0; i < hb->_capacity;i++){
		pcur = hb->_HashTable[i];
		int  new_bucketNober = -1;
		while (pcur)
		{
			hb->_HashTable[i] = pcur->_pNext;
			//插入pcur节点到_newTable中
			new_bucketNober = hb->_pElemToInt(pcur->_data) % new_capacity;
			pcur->_pNext = _newTable[new_bucketNober];
			_newTable[new_bucketNober] = pcur;
			pcur = hb->_HashTable[i];
		}
	}
	free(hb->_HashTable);
	hb->_HashTable = _newTable;
	hb->_capacity = new_capacity;
}

//初始化
void  HashBucketInit(Hash_Bucket *hb, pElemToInt  ElemToInt){
	int i = 0;
	assert(hb);
	hb->_capacity = 3;
	hb->_HashTable = (pHash_Node *)malloc(sizeof(pHash_Node)*(hb->_capacity));
	if (hb->_HashTable==NULL)
	{
		assert(0);
		return;
	}
	for ( i = 0; i < hb->_capacity; i++)
	{
		hb->_HashTable[i] = NULL;
	}
	hb->_size = 0;
	hb->_pElemToInt = ElemToInt;
}
Hash_Node* Buy_Hash_Node(DataType data){
	Hash_Node *ret = (Hash_Node *)malloc(sizeof(Hash_Node));
	if (ret==NULL)
	{
		assert(0);
		return;
	}
	ret->_data = data;
	ret->_pNext = NULL;
	return ret;
}
//插入(元素不重复)
void  HashBucketInsertUnique(Hash_Bucket*hb, DataType data){
	Hash_Node*pcur = NULL;
	Hash_Node*NewNode = NULL;
	assert(hb);
	if (hb->_capacity==hb->_size)
	{
		HashBucketAddCapacity(hb);
	}
	int Hash_Addr = HashFun(hb, data);
	pcur = hb->_HashTable[Hash_Addr];
	while (pcur)
	{
		if (pcur->_data==data)
		  return;
		else
	     pcur = pcur->_pNext;
	}
    NewNode = Buy_Hash_Node(data);
	NewNode->_pNext = hb->_HashTable[Hash_Addr];
	hb->_HashTable[Hash_Addr] = NewNode;
}
//插入(元素可以重复)
void  HashBucketInsertEqual(Hash_Bucket*hb, DataType data){
	Hash_Node*NewNode = NULL;
	assert(hb);
	int Hash_Addr = HashFun(hb, data);
	NewNode = Buy_Hash_Node(data);
	NewNode->_pNext = hb->_HashTable[Hash_Addr];
	hb->_HashTable[Hash_Addr] = NewNode;
}

//删除(元素不重复)
void  HashBucketDeleteUnique(Hash_Bucket*hb, DataType data){
	Hash_Node*pcur = NULL;
	Hash_Node*pre = NULL;
	assert(hb);
	int Hash_Addr = HashFun(hb, data);
	pcur = hb->_HashTable[Hash_Addr];
	while (pcur)
	{
		if (pcur->_data == data)
		{
			if (pcur == hb->_HashTable[Hash_Addr])
			   hb->_HashTable[Hash_Addr] = pcur->_pNext;
			else
			   pre->_pNext = pcur->_pNext;
			free(pcur);
			hb->_size--;
			return;
		}
		else
		{
			pre = pcur;
			pcur = pcur->_pNext;
		}
	}

}

//删除(元素有可能重复)
void  HashBucketDeleteEquel(Hash_Bucket*hb, DataType data){

	Hash_Node*pcur = NULL;
	Hash_Node*pre = NULL;
	assert(hb);
	int Hash_Addr = HashFun(hb, data);
	pcur = hb->_HashTable[Hash_Addr];
	while (pcur)
	{
		if (pcur->_data == data)
		{
			if (pcur == hb->_HashTable[Hash_Addr])
			{
				hb->_HashTable[Hash_Addr] = pcur->_pNext;
				free(pcur);
				pcur = hb->_HashTable[Hash_Addr];
			}
			else
			{
				pre->_pNext = pcur->_pNext;
				free(pcur);
				pcur = pre->_pNext;
			}
			
			hb->_size--;

		}
		else
		{
			pre = pcur;
			pcur = pcur->_pNext;
		}
		
		
	}
}

//找到指定元素
Hash_Node*  HashBucketFind(Hash_Bucket*hb, DataType data){
	Hash_Node*pcur = NULL;
    assert(hb);
	int Hash_Addr = HashFun(hb, data);
	pcur = hb->_HashTable[Hash_Addr];
	while (pcur)
	{
		if (pcur->_data == data)
			return pcur;
		else
			pcur = pcur->_pNext;
	}
	return NULL;
}

//桶的大小
int  HashBucketSize(Hash_Bucket*hb){
	return hb->_size;
}

//桶是否为空
int HashBucketEmpty(Hash_Bucket*hb){
	return 0 == hb->_size;
}

//销毁哈希桶
void HashBucketDestory(Hash_Bucket*hb){
	int i = 0;
	assert(hb);
	for ( i = 0; i < hb->_capacity; i++)
	{
		free(hb->_HashTable[i]);
	}
	free(hb->_HashTable);
	hb->_capacity = 0;
	hb->_size = 0;
}
//打印哈希桶
void HashBucketPrint(Hash_Bucket*hb){
	Hash_Node *pcur = NULL;
	int i = 0;
	assert(hb);
	for (i = 0; i < hb->_capacity; i++)
	{
		pcur = hb->_HashTable[i];
		while (pcur)
		{
			printf("%d---", pcur->_data);
			pcur = pcur->_pNext;
		}
		printf("\n");
   }
}

test.c

#include "HashBucket.h"
#include<stdio.h>
#include<stdlib.h>
int main(){
	Hash_Node* ret;
	Hash_Bucket hb;
	HashBucketInit(&hb, ElemToint);
	HashBucketInsertUnique(&hb,37);
	HashBucketInsertUnique(&hb,25);
	HashBucketInsertUnique(&hb,14);
	HashBucketInsertUnique(&hb,36);
	HashBucketInsertUnique(&hb,49);
	HashBucketInsertUnique(&hb,68);
	HashBucketInsertUnique(&hb,57);
	HashBucketInsertUnique(&hb,11);
	HashBucketInsertEqual(&hb, 14);
	HashBucketInsertEqual(&hb, 68);
	//HashBucketPrint(&hb);
	//HashBucketDeleteEquel(&hb, 14);
	printf("//////////");
	HashBucketPrint(&hb);
	ret = HashBucketFind(&hb, 11);
	printf("%d\n",ret->_data);
	HashBucketDeleteUnique(&hb, 49);
	HashBucketPrint(&hb);
	system("pause");
	return 0;
}

吐舌头总结开散列: 开散列最好的情况是:每个哈希地址存放一个元素

                       开散列最坏的情况是:所有的元素都同一个哈希地址中的链表上,如此下去,随着容量越来越大,就会导致链表的长度很长,而链表的查找时间复杂度为o(N),就会失去哈希原本的效率,所以我们可以将链表改为红黑树,时间复杂度为lg(N).

阅读更多

没有更多推荐了,返回首页