蓝桥备赛(19)- 哈希表和 unordered_ set 与unordered_map(下)

一、哈希表的模拟实现

这里选用的哈希函数就是除留余数法

案例:
题目描述:

输入描述:

测试用例:
12
1 1
1 2
1 3
1 4
1 5
2 2
1 6
2 5
1 7
2 8
2 4
2 25

1.1 线性探测法

1.1.1 创建

一般来说 , 假设数据个数为 n , 我们会创建数组大小为 大于2n 的第一个质数 (因为如果数据密集,存储空间少 , 会出现多次探测)

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

const int INF = 0x3f3f3f3f;
const int N = 23;

//哈希表
int h[N]; 
void init()
{
	memset(h,0x3f,sizeof h);
}
int main()
{
	init();
	return 0;
}

1.1.2 哈希函数以及处理哈希冲突

除留余数法: hash(key) = key % N
但是要注意, key 有可能是负数,取模之后会变成负数
  • 负数补正的操作为:加上模数即可
  • 但是正数加上模数会变大,所以统一再取一次模

//哈希函数
//f(x) 返回 x 的映射地址 
int f(int x) 
{
	//模 加 模
	int id = (x%N+N) % N;
	
	//处理冲突
	while(h[id]!=INF && id!=x)
	{
		id++;
		if(id == N)id = 0;//走到头了,拐个弯 
	 } 
	 return id;
}

1.1.3 添加元素

通过哈希函数找到合适的位置 , 然后放上取即可

 //插入元素 
 void insert(int x)
 {
 	//通过哈希函数找到存储位置 
 	int id = f(x);
 	h[id] = x;
 }

1.1.4 查找元素

通过哈希函数找到映射位置 , 看看里面是不是 x

 
 //查找元素
 void find(int x)
 {
 	int id = f(x);
 	return h[id] == x;
  } 
  

1.1.5 所有测试代码

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

const int INF = 0x3f3f3f3f;
const int N = 23;
int n;
//哈希表
int h[N]; 
void init()
{
	memset(h,INF,sizeof h);
}

//哈希函数
//f(x) 返回 x 的映射地址 
int f(int x) 
{
	//模 加 模
	int id = (x%N+N) % N;
	
	//处理冲突
	while(h[id]!=INF && id!=x)
	{
		id++;
		if(id == N)id = 0;//走到头了,拐个弯 
	 } 
	 return id;
}
 
 //插入元素 
 void insert(int x)
 {
 	//通过哈希函数找到存储位置 
 	int id = f(x);
 	h[id] = x;
 }
 
 //查找元素
 bool find(int x)
 {
 	int id = f(x);
 	return h[id] == x;
  } 
  
int main()
{
	init();
	cin >> n;
	while(n--)
	{
		int op,x;
		cin >> op >> x;
		if(op == 1)
		{
			insert(x);
		}
		else
		{
			if(find(x)) cout << "yes" << endl;
			else cout << "no" << endl;
		}
	}
	return 0;
}

这里就不介绍删除元素了,很复杂~

1.2 链地址法

1.2.1 创建

实现方式与树的链式向前星一样~~~

本质就是用数组模拟链表

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

const int N = 23;
int h[N];//哈希表 
int e[N],ne[N],id;

int main()
{
	return 0;
}

1.2.2 哈希函数

//哈希函数
int f(int x)
{
	//模 加 模 
	return (x%N + N) %N;
 } 

1.2.3 添加元素以及处理哈希冲突

 //添加元素以及处理哈希冲突
 void insert(int x)
 {
 	int idx = f(x);
 	//把x 头插到idx 所在的链表中
	 id++;
	 e[id] = x;
	 ne[id] = h[idx];
	 h[idx]  = id;
 }

1.2.4 查找元素

1)先计算 x 对应的哈希值

2)在哈希值所在的链表中查找

 //查找元素 
 bool find(int x)
 {
 	int idx = f(x);
 	for(int i = h[idx];i;i = ne[i])
 	{
 		if(e[i] == x)return true;
	 }
	 return false;
 }

1.2.5 所有测试代码

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

const int N = 23;
int h[N];//哈希表 
int e[N],ne[N],id;


//哈希函数
int f(int x)
{
	//模 加 模 
	return (x%N + N) %N;
 } 
 
 //插入元素 - 处理哈希冲突 
 void insert(int x)
 {
 	int idx = f(x);
 	//把x 头插到idx 所在的链表中
	 id++;
	 e[id] = x;
	 ne[id] = h[idx];
	 h[idx]  = id;
 }
 
 //查找元素 
 bool find(int x)
 {
 	int idx = f(x);
 	for(int i = h[idx];i;i = ne[i])
 	{
 		if(e[i] == x)return true;
	 }
	 return false;
 }
   
int main()
{
	int n;
	cin >> n;
	while(n--)
	{
		int op,x;
		cin >> op >> x;
		if(op == 1)
		{
			//插入元素
			insert(x); 
		}
		else//查询
		{
			if(find(x))cout << "yes" << endl;
			else cout << "no" << endl; 
		 } 
	}
	return 0;
}

二、unordered set / unordered_multiset

set 与 unordered_set 的区别就是 , 前者是用红黑树实现的 , 后者是用哈希表是实现的

使用方式是完全一样的。无非就是存储和查找的效率不一样 , 以及前者存的是有序 , 后者存的是无序 。

2.1 创建unordered set

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

int main()
{
	unordered_set<int> mp1;
	unordered_set<string> mp2;
	return 0;
}

2.2 size / empty

size : 求哈希表内实际的元素个数 

empty : 判断哈希表是否为空

时间复杂度:O(1)

2.3 begin /end

迭代器 , 可以使用范围 for 遍历哈希表中的所有元素

但是需要注意 , 哈希表不等同于红黑树 , 遍历出来的结果是无序

2.4 insert

往哈希表中插入一个元素

时间复杂度 可以近似认为 : O(1)

2.5 erase

在哈希表中删除一个元素

时间复杂度 可以近似认为 : O(1)

2.6 find/count

1)find : 查找一个元素 , 返回的是迭代器 

2)count : 查询元素出现的次数 , 一般用来判断元素是否在哈希表中 

时间复杂度 可以近似认为 : O(1)

2.7 所有测试代码

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

int main()
{
	string str[] = {"张三","李四","王五","赵六"};
	unordered_set<string> mp;
	
	for(auto& x:str)
	{
		mp.insert(x); 
	 } 
	 
	 if(mp.count("张三") ) cout << "yes" << endl;
	 else cout << "no" << endl;
	 
	 mp.erase("张三"); 
	 if(mp.count("张三") ) cout << "yes" << endl;
	 else cout << "no" << endl;
	 
	 for(auto& x:mp )
	 {
	 	cout << x << " ";
	 }
	return 0;
}

三、unordered map / unordered_multimap

3.1 创建unordered map

#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;

int main()
{
	unordered_map<int,int> mp1;
	unordered_map<string,int> mp1;
	unordered_map<int,string> mp1;
	unordered_map<int,vector<int>> mp1;
	//这个是不是它可以 存图 
	return 0;
}

3.2 size / empty

size : 求哈希表内实际的元素个数 

empty : 判断哈希表是否为空

时间复杂度:O(1)

3.3 begin /end

迭代器 , 可以使用范围 for 遍历哈希表中的所有元素

3.4 insert

往哈希表中插入一个元素

时间复杂度 可以近似认为 : O(1)

3.5 operator[ ]

重载[ ] , 使 unordered_map 可以像数组一样使用

3.6 erase

在哈希表中删除一个元素

时间复杂度 可以近似认为 : O(1)

3.7 find/count

1)find : 查找一个元素 , 返回的是迭代器 

2)count : 查询元素出现的次数 , 一般用来判断元素是否在哈希表中 

时间复杂度 可以近似认为 : O(1)

3.8 所有测试代码

#include <iostream>
#include <vector>
#include <unordered_map>

using namespace std;

void test()
{
	unordered_map<int, vector<int>> mp;
	mp[1].push_back(2);
	mp[2] = {3, 4, 5};
	mp[3].push_back(1);
	mp[3].push_back(2);
	mp[3].push_back(3);

	for(auto& [a, v] : mp)
	{
		cout << a << ": ";
		for(auto b : v) cout << b << " ";
		cout << endl;
	 }
}

int main()
 {
	 test();
	 unordered_map<string, int> mp;

	 // 写一个大括号,把需要放进去的元素括起来即可
	 mp.insert({"张三", 1});
	 mp.insert({"李四", 2});
	 mp.insert({"王五", 3});

	 // operator[]:可以让 map 像数组一样使用 
	 // 赋值
	 mp["赵六"] = 4; // 相当于往 mp里面插入一个 <"赵六", 4>


 	// 查询里面的值
 	if(mp["赵六"] == 4) cout << "yes" << endl;
 	else cout << "no" << endl;
	 // 使用查询的时候要注意,如果 map 中本身没有该元素,它会先插入,然后再拿值
	 // 插入的时候,第1个关键字是默认值
	 // 如果是数,那就是 0
	 // 如果是字符串,那就是空串

 if(mp["小美"]) cout << "no" << endl; // 会把 <"小美", 0> 放进去

 if(mp.count("小帅")) cout << "yes" << endl;
 else cout << "no" << endl;

 mp.erase("小美");
 for(auto& [s, num] : mp)
 {
 cout << s << "编号为: " << num << endl;
 }

 return 0;
 }

四、哈希表算法题

4.1 学籍管理

P5266 【深基17.例6】学籍管理 - 洛谷

#include <iostream>
#include <vector>
#include <unordered_map>

typedef long long LL;
using namespace std;
int n;
unordered_map<string,int> mp; 
 
 int main()
 {
 	cin >> n;
 	while(n--)
 	{
 		int op;
 		string name;
 		LL score; 
 		cin >> op ;
 		if(op == 1)
 		{
 			cin >> name >> score;
 			mp[name] = score;
 			cout << "OK" << endl; 
		 }
		 else if(op == 2)
		 {
		 	cin >> name;
			 if(mp.count(name) )
			 {
			 	cout << mp[name] << endl;
			  } 
			  else
			cout << "Not found" << endl;
		 }
		 else if(op == 3)
		 {
		 	cin >> name;
		 	if(mp.count(name) )
		 	{
		 		mp.erase(name);
				 cout << "Deleted successfully" << endl; 
			 }
			 else{
			 	cout << "Not found" << endl;
			 }
		 }
		 else
		 {
		 	cout << mp.size() << endl;
		 }
	 }
 	return 0;
 }

4.2 不重复数字

P4305 [JLOI2011] 不重复数字 - 洛谷

#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
int main()
{
	int n;
	cin >> n;
	while(n--)
	{
		int p;
		unordered_set<int> mp; 
		cin >> p;
		while(p--)
		{
			int x;
			cin >> x;
			if(!mp.count(x))//是第一次出现
			{
				cout << x << " ";
				mp.insert(x);
			 } 
		}
		cout << endl;
	}
	return 0;
}

处理以下 cin  和 cout  

蓝桥备赛(六)- C/C++输入输出-优快云博客

#include <iostream>
#include <vector>
#include <unordered_set>
#include <cstdio>
using namespace std;
int main()
{
	int n;
	//cin >> n;
	scanf("%d",&n);
	while(n--)
	{
		int p;
		unordered_set<int> mp; 
		//cin >> p;
		scanf("%d",&p);
		while(p--)
		{
			int x;
			scanf("%d",&x);
			if(!mp.count(x))//是第一次出现
			{
				//cout << x << " ";
				printf("%d ",x);
				mp.insert(x);
			 } 
		}
//		cout << endl;
		puts(" ");
	}
	return 0;
}

4.3 阅读理解

P3879 [TJOI2010] 阅读理解 - 洛谷

#include <iostream>
#include <set>
#include <unordered_map>
using namespace std;

//标记单词在那些文章出现过 
unordered_map<string,set<int>> mp;

int main()
{
	int n;
	cin >> n;
	for(int i = 1;i<=n;i++)
	{
		int l;cin >> l;
		while(l--)
		{
			string s;
			cin >> s;
			mp[s].insert(i) ;	
		} 
	}
	
	int m;
	cin >> m;
	while(m--)
	{
		string s;
		cin >> s;
		for(auto e:mp[s])
		{
			cout << e << " ";
		}	
		cout << endl;
	}	
	return 0;
}

4.4 A-B数对

P1102 A-B 数对 - 洛谷

#include <iostream>
#include <set>
#include <unordered_map>
using namespace std;

typedef long long LL;
const int N = 2e5 + 10;
LL n,c;
LL a[N];
unordered_map<int,int> mp;

int main()
{
	cin >> n >> c;
	for(int i = 1;i<=n;i++)
	{
		cin >> a[i];
		mp[a[i]]++;
	}
	LL ret = 0;
	for(int i = 1;i<=n;i++)
	{
		//b = a[i]
		//c + a[i]
		ret += mp[c + a[i]];
	}
	cout << ret << endl;
	return 0;
}

4.5 Cities and States

P3405 [USACO16DEC] Cities and States S - 洛谷

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

//<拼接后的对应关系,次数>
unordered_map<string,int> mp;
int main()
{
	int n;
	cin >> n;
	int ret = 0;
	while(n--)
	{
		string a,b;
		cin >> a >> b;
		a = a.substr(0,2);
		if( a == b ) continue;
		ret += mp[b+a];//统计b->a有多少个 
		mp[a+b]++; 
	}
	cout << ret << endl; 
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值