Hash相关(从哈希表到哈希查找)


        第一节——初赛中的Hash

        哈希表,也称散列表,是一种高效的数据结构。它的最大优点就是把数据存储和查找所消耗的时间大大降低,几乎可以看成是O(1)的,而代价是消耗比较多的内存。在当前竞赛可利用内存空间越来越多、程序运行时间控制的越来越近的情况下,“空间换时间”的做法还是很值得的。

        哈希冲突,不能保证每个元素的关键字与函数值是一一对应的,这样就产生了“冲突”。

        解决冲突方法:

        1、直接取余数法      h(k)=k  mod  m     //m一般为一个大素数

              关键字k除以m,取余数作为在Hash表中的位置。

        2、乘积取整法;

        3、平方取中法;

        4、线性探测法

        5、链地址法(后两种会在后面详细讲到,第二种和第三种可以自行了解,不强求)

 

        查找算法

        基本的查找算法有以下4种:

                1、线性查找(挨个看呗)

                2、二分查找(log级别每次折半查找,前提是所有元素必须是顺序排列的)

                3、分块查找(块与块有序、块内无序,可以理解成块外用二分、块内用线性)

                4、哈希查找(他来了,他来了!!)

        哈希查找简介

                1、哈希是Hash的音译,意译就是散列。哈希查找是一种按关键字地址的快速检索方法。

                2、哈希与其他查找方法的不同之处

                        (1)哈希查找是对记录的关键字值进行某种运算,直接求出记录的地址

                        (2)快——关键字到地址直接转换,无序反复比较

                        (3)核心不在于如何“比较”,而在于如何“计算”

                        (4)最能体现计算机科学精髓的查找方法

        哈希查找的基本思想:记录关键字与其存储地址之间的映射关系H(key),把某个较大的集合P映射到另一个较小的集合Q中,核心就是设计哈希函数,并构建哈希表。

        哈希查找过程:

                1、由给定的关键字key,根据哈希函数计算出对应的哈希地址H(key)

                2、根据H(key)直接找到该记录的存储位置

        哈希冲突与解决(系不系很多童鞋们都肥肠期待!!??)

        当两个不同的数据的哈希值相同时,就会发生冲突(collision)

        处理哈希冲突的常用方法之一:链地址法

        链地址法具体步骤:

                (1)将哈希值相同的数据存在一个链表中

                (2)查找哈希表时,当查找到这个链表时必须采用线性查找方法

        哈希函数典型构造方法——求模取余法

                (1)H(key)=key % p

                (2)p<=m,哈希表表长为m

                (3)余数(哈希地址)总是循环出现,呈周期性变化

        哈希表的特点

哈希表与一般的线性表的区别
一般的线性表 哈希表

在记录的存储地址与它的关键字之间不存在确定的对应关系

在记录的存储地址和它的关键字之间存在一个确定的对应关系

在表中查找记录时,需要进行一系列 的关键字比较

每个关键字在表中有唯一的一个存储地址与之对应,直接由关键字找到存储地址

建立在“比较“的基础上,查找效率依赖于查找过程中所进行的比较次数

 

根据关键字直接计算出记录存放的地址查找效率高

 

 

 

 

 

 

 

 

 

 

        第二节——Hash查询代码实现

        哈希典型题

        哈希构造的链表中查询方式比较快。已知存在一个随机的数据表(表中数字均为正整数,没有重复),数据个数n≤50000;现在给定一些整数,查询在表中是否存在。

        二分代码实现(代码短,时间长)

 

#include<iostream>
#include<algorithm>
using namespace std;

int a[10005],l,r,mid;

int main()
{
	int key,n,m;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	sort(a,a+n+1);
	while(m--)
	{
		cin>>key;
		l=1;
		r=n;
		while(l<=r)
		{
			mid=(l+r)/2;
			if(a[mid]==key)
			{
				cout<<"yes/n";
				break;
			}
			if(a[mid]<key)
			{
				l=mid+1;
			}
			if(a[mid]>key)
			{
				r=mid-1;
			}
		}
		if(a[mid]!=key)
		{
			cout<<"no/n";
		}
	}
	return 0;
}

 

  哈希代码实现(代码长、时间短)

#include<iostream>
using namespace std;

const int N=50000;
const int b=999979,H=999979;
int tot,adj[H],nxt[N],num[N];
int top,stk[N];

void init()
{
	tot=0;
	while(top)
	{
		adj[stk[top--]]=0;
	}
}

void insert(int key)
{
	int h=key%b;
	for(int e=adj[h];e;e=nxt[e])
	{
		if(num[e]==key)
		{
			return ;
		}
	}
	if(!adj[h])
	{
		stk[++top]=h;
	}
	nxt[++tot]=adj[h],adj[h]=tot;
	num[tot]=key;
}

bool query(int key)
{
	int h=key%b;
	for(int e=adj[h];e;e=nxt[e])
	{
		if(num[e]==key)
		{
			return true
		}
	}
	return false;
}

int main()
{
	int a[10000];
	init();
	int n,m;
	cin>>n;
	for(int i=1;i<=n;++i)
	{
		cin>>a[i];
		insert(a[i]);
	}
	cin>>m;
	int num;
	for(int i=1;i<=m;i++)
	{
		cin>>num;
		if(query(num))
		{
			cout<<"yes"<<endl;
		}
		else
		{
			cout<<"no"<<endll;
		}
	}
	return 0;
}

  第三节——哈希冲突解决

        1、顺序寻址法

        设H(key)=key%9

        若当前位置已经有原先的数据了,则放到下一个位置去,如果下一个位置仍然不为空,那么久继续往后走,直到找到第一个为空的元素位置把该元素放进去。

        代码实现

int hash_table[N];
void push1(int x)
{
	int y=x%N;
	for(;hash_table[y]&&hash_table[y]!=x;)
	{
		y=(y+1)%N;
	}
	if(hash_table[y])
	{
		cout<<x<<"_has_occured_before!"<<endl;
	}
	else
	{
		hash_table[y]=x;
		cout<<x<<"_inserted."<<endl;
	}
}

  2、链地址法(刚才说过了,不再多说了,直接上代码)

        代码实现(用到了动态数组vector,在STL里面,不知道的可以去百度上搜搜教程)

 

        推荐题目(洛谷):P1102、P3370、P2957、P1955、P13381、P5018

 

原创文章,作者:端木书台,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/273696.html

(0)
上一篇 2022年7月11日 23:10
下一篇 2022年7月11日 23:11

相关推荐

发表回复

登录后才能评论