unordered_set的底层是哈希表。
增删改查的时间复杂度:
数组 O(n) 二叉树O(logn) 哈希表 O(1)
哈希表的本质原理:哈希键--(哈希函数)--哈希值--(取模、位于)--桶/ID
这里的哈希键一般是任意类型,所以需要先通过哈希函数转换为整数,我们叫他哈希值,再通过取模(一般使用的时候采用位于运算),映射到某个桶中。这样就可以把任意类型的数据存储到数组中,且能够快速查找到。
桶:下标索引又叫桶ID,桶ID是用来索引到某个具体位置的桶。因为用的取模,所以得到的桶ID都是连续的自然数,这样桶可以用数组来实现
哈希冲突:是指两个元素的键不同,但是它们计算得到桶ID一样的情况
哈希冲突解决方案:常见的解决方案,是在一个桶中放多个元素,这样就可以通过哈希算法快速找到桶,再通过遍历桶中所有元素,来实现快速查找。在STL中,数据不是存在一个bucket桶中,而是利用了(bucket<<1)和(bucket<<1 )+ 1 两个位置,并且把所有桶中元素串联成一个链表,它里面的元素,是从(bucket<<1)的位置作为链表的起点,(bucket<<1 )+ 1的位置作为链表终点,来存储哈希表中所有桶ID为bucket的节点。所有的哈希表节点会被链接到一个全局的双向链表中,双向链表有个虚拟头结点_Head
哈希表的增删查:哈希表的元素插入,便是直接插入到头部。哈希表的元素查找:从后往前来查找。哈希表的元素删除:先找到桶ID,然后从后往前逆序遍历,执行双向链表的删除操作。
散列容器:unordered_set unordered_map
线形容器:vector、string、list
树形容器:set、multiset、map、multimap
线性模拟树:priority_queue
unordered_set 对象创建,代码见下
#include
#include
using namespace std;
void printUSet(const unordered_set& s) {
for (unordered_set::const_iterator it = s.begin(); it != s.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
// 1 默认构造函数
unordered_set s1;
cout << "s1: ";
printUSet(s1);
// 2 初始化列表
unordered_set s2_1 = { 9, 8, 7, 6, 5, 1, 2, 3, 4, 6 };
cout << "s2_1: ";
printUSet(s2_1);
unordered_set s2_2 ({ 9, 8, 7, 6, 5, 1, 2, 3, 4, 6, 6, 6, 6, 6 });
cout << "s2_2: ";
printUSet(s2_2);
// 3 迭代器
unordered_set s3(s2_1.begin(), s2_1.end());
cout << "s3: ";
printUSet(s3);
// 4 拷贝构造
unordered_set s4(s2_2);
cout << "s3: ";
printUSet(s3);
return 0;
}
unordered_set 赋值操作,代码见下
#include
#include
using namespace std;
void printUSet(const unordered_set& s) {
for (unordered_set::const_iterator it = s.begin(); it != s.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
unordered_set s = {9, 8, 5, 2, 1, 1};
cout << "s: ";
printUSet(s);
// = set对象
unordered_set s1;
s1 = s;
cout << "s1: ";
printUSet(s1);
// = 初始化列表
unordered_set s2;
s2 = { 9,3, 4, 6, 1, 3 };
cout << "s2: ";
printUSet(s2);
return 0;
}
unordered_set 大小操作,代码见下
#include
#include
using namespace std;
void printUSet(const unordered_set& s) {
for (unordered_set::const_iterator it = s.begin(); it != s.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
unordered_set s1;
cout << "s1.empty()" << s1.empty() << endl;
cout << "s1.size()" << s1.size() << endl;
unordered_set s2 = {5, 6, 8, 3, 1, 3};
cout << "s2.empty()" << s2.empty() << endl;
cout << "s2.size()" << s2.size() << endl;
unordered_multiset s3 = { 5, 6, 8, 3, 1, 3, 1, 1, 1 };
cout << "s3.empty()" << s3.empty() << endl;
cout << "s3.size()" << s3.size() << endl;
return 0;
}
结果见下,助于理解
unordered_set 数据插入,代码见下
#include
#include
using namespace std;
void printUSet(const unordered_set& s) {
for (unordered_set::const_iterator it = s.begin(); it != s.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
unordered_set s1;
s1.insert(5); printUSet(s1);
s1.insert(4); printUSet(s1);
s1.insert(7); printUSet(s1);
s1.insert(1); printUSet(s1);
s1.insert(2); printUSet(s1);
s1.insert(6); printUSet(s1);
s1.insert(9); printUSet(s1);
vector v = { 1, 9, 9, 0 };
s1.insert(v.begin(), v.end());
printUSet(s1);
return 0;
}
结果见下,助于理解:
5
5 4
5 4 7
5 4 7 1
5 4 7 1 2
5 4 7 1 2 6
5 4 7 9 1 2 6
5 4 7 9 1 2 6 0
最后一行这个结果只是将不重复的0插入了set中,具体原因可见源码截图
unordered_set 数据查找,代码见下
#include
#include
using namespace std;
void printUSet(const unordered_set& s) {
for (unordered_set::const_iterator it = s.begin(); it != s.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
unordered_set s = { 4, 3, 7, 9, 1 };
unordered_set::iterator it = s.find(3);
if (it != s.end()) {
cout << "find: " << *it << endl;
}
it = s.find(10);
if (it == s.end()) {
cout << "can't find 10" << endl;
}
return 0;
}
unordered_set 数据删除,代码见下
#include
#include
using namespace std;
void printUSet(const unordered_set& s) {
for (unordered_set::const_iterator it = s.begin(); it != s.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
unordered_set s = { 8, 4, 3, 7, 1 };
s.erase(3);
printUSet(s);
unordered_set::iterator it = s.find(4);
if (it != s.end()) {
s.erase(it);
}
printUSet(s);
s = { 13, 14, 56, 72, 34, 45, 23, 67 };
unordered_set::iterator it1 = s.find(23);
unordered_set::iterator it2 = s.find(14);
//s.erase(it1, it2); //不建议用
printUSet(s);
s.clear();
return 0;
}
unordered_set 数据统计,代码见下
#include
#include
using namespace std;
void printUSet(const unordered_set& s) {
for (unordered_set::const_iterator it = s.begin(); it != s.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
unordered_set s = { 5, 6, 7, 8, 9 };
for (int i = 0; i < 8; i += 2) {
cout << "元素:" << i << "的出现次数为" << s.count(i) << endl;
}
printUSet(s);
return 0;
}
代码练习 1,对应力扣,相交链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set us;
while(headA){
us.insert(headA);
headA = headA -> next;
}
while(headB){
if(us.find(headB) != us.end()){
return headB;
}
headB = headB->next;
}
return NULL;
}
};
代码练习 2,对应力扣,重复DNA序列
class Solution {
public:
vector findRepeatedDnaSequences(string s) {
unordered_multiset ums;
vector ans;
for(int i=0; i+10 <= s.size(); ++i){
string sub = s.substr(i, 10);
ums.insert(sub);
if(ums.count(sub) == 2){
ans.push_back(sub);
}
}
return ans;
}
};