unordered_map、unordered_set使用
实现机理
unordered_map内部实现了一个哈希表,也叫散列表,通过把关键码值映射到Hash表中一个位置来访问记录,查找的时间复杂度可达到O(1),其在海量数据处理中有着广泛应用。因此,其元素<key,value>的排列顺序是无序的。
unordered_set底层也是哈希表,只是存储的是value,而不是<key,value>
待补充:
unordered_map使用
参考:
http://www.cplusplus.com/reference/unordered_map/unordered_map/
http://c.biancheng.net/view/530.html
- 基本:初始化,增删改查;
类模板声明
template < class Key, // unordered_map::key_type
class T, // unordered_map::mapped_type
class Hash = hash<Key>, // unordered_map::hasher
class Pred = equal_to<Key>, // unordered_map::key_equal
class Alloc = allocator< pair<const Key,T> > // unordered_map::allocator_type
> class unordered_map;
注意: unordered_set和unordered_map本质上都是使用hash方法对元素进行存储和查找,而C++没有为vector,pair等定义默认hash方法,所以模板参数不能是vector,pair这类。除非你自己自定义一个对应的hash函数
头文件
#include < unordered_map >
初始化
//默认无参构造
explicit unordered_map ( size_type n = /* see below */,
/*size_type n: Minimum number of initial buckets.没有设定会自适应 */
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type() );
explicit unordered_map ( const allocator_type& alloc );
//迭代器范围构造
template <class InputIterator>
unordered_map ( InputIterator first, InputIterator last,
size_type n = /* see below */,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type() );
//复制构造
unordered_map ( const unordered_map& ump );
unordered_map ( const unordered_map& ump, const allocator_type& alloc );
//移动语义的复制构造
unordered_map ( unordered_map&& ump );
unordered_map ( unordered_map&& ump, const allocator_type& alloc );
//初始化列表构造
unordered_map ( initializer_list<value_type> il,
size_type n = /* see below */,
/*size_type n: Minimum number of initial buckets.没有设定会自适应 */,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type() );
示例:
// constructing unordered_maps
#include <iostream>
#include <string>
#include <unordered_map>
typedef std::unordered_map<std::string,std::string> stringmap;
stringmap merge (stringmap a,stringmap b) {
stringmap temp(a); temp.insert(b.begin(),b.end()); return temp;
}
int main ()
{
stringmap first; // empty
stringmap second ( {{"apple","red"},{"lemon","yellow"}} ); // init list
stringmap third ( {{"orange","orange"},{"strawberry","red"}} ); // init list
stringmap fourth (second); // copy
stringmap fifth (merge(third,fourth)); // move
stringmap sixth (fifth.begin(),fifth.end()); // range
std::cout << "sixth contains:";
for (auto& x: sixth) std::cout << " " << x.first << ":" << x.second;
std::cout << std::endl;
return 0;
}
访问某个元素
先构造
//空的构造函数
std::unordered_map<std::string, int> mymap0;
//初始化列表构造
std::unordered_map<std::string, int> mymap = { { "one", 1 }, { "two", 2 }, { "three", 3 } };
再访问
- 下标运算符中使用键来获取它所对应的值的引用,在下标中使用不存在的键时,会以这个键为新键生成一个新的元素,新元素的值是默认的。
mymap["one"]=0;//将"one"对应的值改为0
mymap["zero"];//会在mymap中增加一个键值对{"zero",0}
- 成员函数 at() 会返回参数所关联对象的引用,如果键不存在,会拋出一个 out_of_range 异常。所以当我们不想生成有默认对象的元素时,应该选择使用 at() 而不是下标运算符。
mymap.at("one")=1;//将"one"对应的值改为1
mymap.at("three");//拋出一个 out_of_range 异常
查找
- count:通过key查找,找到返回1,找不到返回0。
size_type count ( const key_type& k ) const;
- find():返回key对应的迭代器,如果key不存在,则find返回unordered_map::end因此可以通过判断map.find(key) == map.end()来判断,key是否存在于当前的unordered_map中,
iterator find ( const key_type& k );
注意: unordered_map中的value_type是pair对象,所以迭代器指向的是pair对象
template <class T1, class T2> struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair() : first(T1()), second(T2()) {}
pair(const T1& x, const T2& y) : first(x), second(y) {}
template <class U, class V>
pair (const pair<U,V> &p) : first(p.first), second(p.second) { }
}
template <class T1, class T2>
pair<V1,V2> make_pair (T1&& x, T2&& y); // see below for definition of V1 and V2
//The function returns:
template <class T1, class T2>
pair<V1,V2> make_pair (T1&& x, T2&& y){
return pair<V1,V2>(std::forward<T1>(x),std::forward<T2>(y));
}
//使用示例
pair<int ,int >p (5,6);
//函数参数是pair时, make_pair()比较方便
pair<int ,int > p1= make_pair(5,6);
//unordered_map中的value_type如下
typedef pair<const Key, T> value_type;
unordered_map<Key,T>::iterator it= find ( const key_type& key );
it->first; // key
it->second; // value)
遍历元素
- 基于迭代器遍历输出
//auto自动识别为迭代器类型
for (auto it = mymap.begin(); it != mymap.end(); ++it)
std::cout << " " << it->first << ":" << it->second << std::endl;
//注意如果用operator*(),则需要注意优先级,(*it)必须加括号,记住教训
for (auto it = mymap.begin(); it != mymap.end(); ++it)
std::cout << " " <<(*it).first << ":" << (*it).second << std::endl;
- 基于范围的for循环,遍历元素
for (auto& x : mymap) {
std::cout << x.first << ": " << x.second << std::endl;
}
- 查看每个bucket中的元素
std::cout << "mymap's buckets contain:\n";
for (unsigned i = 0; i < mymap.bucket_count(); ++i) {
std::cout << "bucket #" << i << " contains:";
for (auto local_it = mymap.begin(i); local_it != mymap.end(i); ++local_it)
std::cout << " " << local_it->first << ":" << local_it->second;
std::cout << std::endl;
}
插入
注意: map中的value_type是pair对象,所以迭代器指向的是pair对象
pair<iterator,bool> insert ( const value_type& val );
template <class P>
pair<iterator,bool> insert ( P&& val );
返回值为pair<set::iterator, bool>
iterator表示该元素的位置 ,
bool 为true,表示插入成功(即原来set中没有此插入的元素)
bool为false,表示插入失败(即原来set中已存在此插入的元素)
//定义插入元素,类型为pair的对象
std::pair<std::string,int> item("four",4);
mymap.insert(item);
//利用make_pair<std::string,int>插入
mymap.insert(std::make_pair<std::string,int>("five",5);
//范围插入
unordered_map<std::string,int> yourmap{{"ten",10},{"eleven",11},{"twelve",12}};
mymap.insert(yourmap.begin(),yourmap.end());
// 初始化列表插入
mymap.insert({{"thirteen",13},{"fourteen",14}});
删除
// 通过迭代器删除
mymap.erase(mymap.begin());
// 通过 Key 值删除
mymap.erase("one");
// 通过迭代器范围删除
mymap.erase(mymap.find("three"), mymap.end());
//清空mymap容器
mymap.clear();
leetcode例题
- 多数元素
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2
最简单的一种方法就是利用哈希表统计元素出现的频率,然后找出出现频率最大的那个元素
class Solution {
public:
int majorityElement(vector<int>& nums) {
unordered_map<int,int> counts;
int majority,max=0;
for(auto i:nums){
counts[i]++;
if(counts[i]>max){
max=counts[i];
majority=i;
}
}
return majority;
}
};
unordered_set使用
类模板声明
template < class Key, // unordered_set::key_type/value_type
class Hash = hash<Key>, // unordered_set::hasher
class Pred = equal_to<Key>, // unordered_set::key_equal
class Alloc = allocator<Key> // unordered_set::allocator_type
> class unordered_set;
注意: unordered_set和unordered_map本质上都是使用hash方法对元素进行存储和查找,而C++没有为vector,pair等定义默认hash方法,所以模板参数不能是vector,pair这类。除非你自己自定义一个对应的hash函数
头文件
#include<unordered_set>
初始化
参考:http://www.cplusplus.com/reference/unordered_set/unordered_set/unordered_set/
//默认无参构造
explicit unordered_set ( size_type n = /* see below */,
/*size_type n: Minimum number of initial buckets.没有设定会自适应 */
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type() );
//迭代器范围构造
template <class InputIterator>
unordered_set ( InputIterator first, InputIterator last,
size_type n = /* see below */,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type() );
//复制构造
unordered_set ( const unordered_set& ust );
unordered_set ( const unordered_set& ust, const allocator_type& alloc );
//移动语义的复制构造
unordered_set ( unordered_set&& ust );
unordered_set ( unordered_set&& ust, const allocator_type& alloc );
//初始化列表
unordered_set ( initializer_list<value_type> il,
size_type n = /* see below */,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& alloc = allocator_type() );
- 使用示例
// constructing unordered_sets
#include <iostream>
#include <string>
#include <unordered_set>
template<class T>
T cmerge (T a, T b) {
T t(a);
t.insert(b.begin(),b.end());
return t;
}
int main ()
{
std::unordered_set<std::string> first; // empty
std::unordered_set<std::string> second ( {"red","green","blue"} ); // init list
std::unordered_set<std::string> third ( {"orange","pink","yellow"} ); // init list
std::unordered_set<std::string> fourth ( second ); // copy
std::unordered_set<std::string> fifth ( cmerge(third,fourth) ); // move
std::unordered_set<std::string> sixth ( fifth.begin(), fifth.end() ); // range
std::cout << "sixth contains:";
for (const std::string& x: sixth) std::cout << " " << x;
std::cout << std::endl;
return 0;
}
查找
可以访问,不能修改,可以删除
- find, 找到,返回iterator;没有找到,返回end()。
iterator find ( const key_type& k );
//示例
std::unordered_set<std::string> nums({"one","two","three"});
std::unordered_set<std::string>::const_iterator it=nums.find("one");
if ( it == nums.end() )
std::cout << "not found in nums";
else
std::cout << *it << " is in nums";
- count,找到,返回1;没有找到,返回0。
size_type count ( const key_type& k ) const;
//使用示例
std::unordered_set<std::string> nums({"one","two","three"});
if (nums.count("one")>0)
std::cout << "nums has " << x << std::endl;
else
std::cout << "nums has no " << x << std::endl;
遍历
- 基于迭代器
由于unordered_set内部是无序的,所以begin(),end()就只保证从begin()到end()的范围覆盖了所以元素
std::unordered_set<std::string> nums({"one","two","three"});
for ( auto it = nums.begin(); it != nums.end(); ++it )
std::cout << " " << *it;
- 基于for范围循环遍历
for(auto& i:nums)
cout<<" "<<i;
- 遍历bucket中的元素
for ( unsigned i = 0; i < nums.bucket_count(); ++i) {
std::cout << "bucket #" << i << " contains:";
for ( auto local_it = nums.begin(i); local_it!= nums.end(i); ++local_it )
std::cout << " " << *local_it;
}
插入
- insert
返回值为pair<set::iterator, bool>
iterator表示该元素的位置 ,
bool 为true,表示插入成功(即原来set中没有此插入的元素)
bool为false,表示插入失败(即原来set中已存在此插入的元素)
//插入值
pair<iterator,bool> insert ( const value_type& val );
pair<iterator,bool> insert ( value_type&& val );
//从建议的位置hint开始搜索插入点, 一般不用
iterator insert ( const_iterator hint, const value_type& val );
iterator insert ( const_iterator hint, value_type&& val );
//范围插入
template <class InputIterator>
void insert ( InputIterator first, InputIterator last );
//插入初始化列表
void insert ( initializer_list<value_type> il );
std::unordered_set<std::string> nums({"one","two","three"});
nums.insert("four");
nums.insert({"five","six"});
删除
- erase
//通过位置删除
iterator erase ( const_iterator position );
//通过key删除
size_type erase ( const key_type& k );
//范围删除
iterator erase ( const_iterator first, const_iterator last );
- clear()
清空
leetcode例题
653. 两数之和 IV - 输入 BST
给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。
方法: 使用 unordered_set,一次遍历
设给定的和为k,对于每个值为 p 的节点,在 unordered_set 中检查是否存在k−p。如果存在,那么可以在该树上找到两个节点的和为 k;否则,将 p 放入到 unordered_set 中。
如果遍历完整棵树都没有找到一对节点和为 k,那么该树上不存在两个和为 k 的节点。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool findTarget(TreeNode* root, int k) {
//unordered_set 一次遍历
unordered_set<int> ans;
return findTargetCore(root,ans,k);
}
bool findTargetCore(TreeNode* root,unordered_set<int> &ans,int k){
if(root==NULL)
return false;
if(ans.find(k-root->val)!=ans.end())
return true;
ans.insert(root->val);
return findTargetCore(root->left,ans,k)||findTargetCore(root->right,ans,k);
}
};
1496. 判断路径是否相交
1496. 判断路径是否相交
给你一个字符串 path,其中 path[i] 的值可以是 ‘N’、‘S’、‘E’ 或者 ‘W’,分别表示向北、向南、向东、向西移动一个单位。
机器人从二维平面上的原点 (0, 0) 处开始出发,按 path 所指示的路径行走。
如果路径在任何位置上出现相交的情况,也就是走到之前已经走过的位置,请返回 True ;否则,返回 False 。
1 <= path.length <= 10^4
path 仅由 {‘N’, ‘S’, ‘E’, 'W} 中的字符组成
class Solution {
public:
vector<int> nextPos(int x,int y,char c){
if(c=='N'){
return {x,y+1};
}else if(c=='S'){
return {x,y-1};
}else if(c=='E'){
return {x+1,y};
}else{
return {x-1,y};
}
}
bool isPathCrossing(string path) {
//利用unordered_map记录走过的坐标
int N=path.size();
if(0==N) return false;
//这两种用法都不支持,没有对应的hash函数
//unordered_set<pair<int,int>> ms;
//unordered_set<vector<int>> ms;
//曲线救国,将x,y转成字符串总行了吧
unordered_set<string> ms;
ms.insert("00");
int x=0,y=0;
for(int i=0;i<N;i++){
vector<int> nextP=nextPos(x,y,path[i]);
x=nextP[0];
y=nextP[1];
if(ms.count(to_string(x)+to_string(y)))
return true;
else
ms.insert(to_string(x)+to_string(y));
}
return false;
}
};