上次说好了写哈希表,但是只写哈希表好像内容又有点少,加一个大/小顶堆好了。虽然这俩好像关联不大哈哈哈
一、哈希表——无序图
C++11标准中并未加入hash_map的结构,那么哈希表的实现呢,我们用的是无序图(unordered_map)这个结构。unordered_map与集合同为关联容器,不同之处在于它的内部元素是无序的,是采用哈希表的结构来存储key的hash值。
1. 定义与初始化
使用时需要带头文件#include <unordered_map>
定义时需要明确键值和映射值的数据类型,映射值也可以是数组、链表等类型,可以用来存储键对应的多个数据。
unordered_map<char,int> freq;
unordered_map<char,int> freq1 (freq); //拷贝初始化
unordered_map<char,int> freq2 (freq.begin(),freq.end()); //范围初始化
2. 读取数据
再次强调,关联容器不能按位置访问元素,而且无序图内部元素是无序的。按键值查找或者插入的效率都很高。
一个常用的例子:求字符串中各字符出现的次数
string str="helloworld";
unordered_map<char,int> freq;
for(char i:str)
freq[i]++; //没有的键会直接插入,映射值初始化为0,计数非常方便
//cout<<freq['o']; //单独访问,直接查找键值
for(auto [k,v]:freq) //遍历,k为键值,v为映射值,注意是方括号
cout<<k<<"-"<<v<<endl;
运行结果:
r-1
e-1
d-1
o-2
h-1
w-1
l-3
3. 成员函数
bool isEmpty = freq.empty(); //判断是否为空
int num = freq.size(); //返回有效元素个数
freq.insert(pair<char,int>); //插入元素,参数为类型一致的pair
freq.erase(key); //删除元素,参数为键值
freq.clear(); //清空内容
int cnt = freq.count(); //返回匹配给定主键的元素的个数,只可能为0或1,用来判断某一键值是否存在
接着之前的例子:
pair<char,int> a('a',-1);
freq.insert(a);
freq.erase('l');
for(auto [k,v]:freq) //遍历,k为键值,v为映射值
cout<<k<<"-"<<v<<endl;
运行结果:
r-1
e-1
d-1
o-2
h-1
a--1
w-1
二、大/小顶堆——优先队列
优先队列,顾名思义是一种特殊的队列,它的特殊之处就在于我们可以自定义队列中数据的优先级,让队列内元素按照优先级排列。
1.定义
既然是队列的一种,那么使用时当然需要带头文件#include <queue>
priority_queue<int> q; //默认为最大堆/大顶堆
priority_queue<int, vector<int>, greater<int>> q2; //小顶堆
如果数据类型不能直接比较,可以自定义排序方法:
struct cmp{
bool operator ()(const vector<int>& a, const vector<int>& b)
{
//注意:方法中定义用的大于,最后得到的是小顶堆,这个非常容易搞混,切记切记
return a[1]>b[1] || a[1]==b[1] && a[2]>b[2];
}
};
priority_queue<vector<int>, vector<vector<int>>, cmp> q;
类似地,如果是自定义的数据类型,也可以通过 “<” 运算符重载的方法来排序。举个栗子:
#include <iostream>
#include <queue>
using namespace std;
struct Node{ //自定义数据结构-二维坐标
int x, y;
Node(int a=0, int b=0):x(a), y(b) {}
};
bool operator<( Node a, Node b ){ //注意:"<"对应的是大顶堆,比如 a<b<c对应的是c在堆顶
return a.x>b.x || a.x==b.x && a.y>b.y;
}
int main(){
priority_queue<Node> q;
q.push(Node(5, 5));
q.push(Node(1, 2));
q.push(Node(3, 5));
q.push(Node(3, 2));
q.push(Node(5, 1));
while(!q.empty())
{
cout<< q.top().x <<","<< q.top().y <<endl;
q.pop();
}
return 0;
}
运行结果:
1,2
3,2
3,5
5,1
5,5
2. 常用方法
和队列的常用方法基本相同,都是基础操作,就不举例子了。
//判空
q.empty()
//大小
q.size()
//插入
q.push(i)
//出队,即弹出最大值
q.pop()
//堆顶,即取最大值
q.top()
总结
哈希表可太常用了,用法一定一定要熟记。大/小顶堆在一些问题中可以得到很巧妙的解决方法。