C++中的hash table
- C++ STL中有四种关联式容器, map ,set, multimap, multiset; 他们底层是RB树, 查找的时间复杂度是O(logN)
- 他们都有对应的hash table版本, unordered_map, unordered_set, unordered_multimap, unordered_multiset. 他们底层的实现是hash table, 查找时间复杂度O(1).
- 解决冲突的方法是
链地址法
- 链地址法 (用一个链表存储所有冲突的记录), 开放地址法 (有冲突, 以当前位置为基准根据探查序列再寻址), 再哈希法
自定义HashSet
- 一个vector, 每个容器都维持着一个链表, 初始化NULL
- vector的下标就是对应的hash_key
- vector存放的是每个链表的头节点, 每个函数都需要
单独判断.
- 删除操作, 需要双指针, 记录删除结点的前一个结点
class MyHashSet
{
public:
struct ListNode
{
int val;
ListNode* next;
ListNode(int v): val(v), next(NULL){}
};
int range;
vector<ListNode*> bucket_v;
MyHashSet()
{
this->range = 769;
bucket_v.assign(this->range, 0);
}
void add(int key)
{
int hash_key = key % this->range;
ListNode* node = new ListNode(key);
if (bucket_v[hash_key] == NULL) bucket_v[hash_key] = node;
else
{
ListNode* temp = bucket_v[hash_key];
if (temp->val == key) return;
while(temp->next != NULL)
{
temp = temp->next;
if (temp->val == key) return ;
}
temp->next = node;
}
}
void remove(int key)
{
int hash_key = key % this->range;
if (bucket_v[hash_key] == NULL) return ;
else
{
ListNode* pre = bucket_v[hash_key];
if (pre->val == key)
{
bucket_v[hash_key] = pre->next;
return ;
}
ListNode* cur = pre->next;
while(cur != NULL)
{
if (cur->val == key)
{
pre->next = cur->next;
return ;
}
pre = cur;
cur = cur->next;
}
}
}
bool contains(int key)
{
int hash_key = key % this->range;
ListNode* cur = bucket_v[hash_key];
while(cur != NULL)
{
if (cur->val == key)
return true;
cur = cur->next;
}
return false;
}
};
自定义实现LRU
class LRUCache
{
public:
unordered_map<int, list<pair<int, int>>::iterator> mp;
list<pair<int, int>> cache;
int capacity;
LRUCache(int val)
{
this->capacity = val;
}
int get(int key)
{
auto mp_iter = mp.find(key);
if (mp_iter == mp.end())
return -1;
auto list_iter = mp_iter->second;
int val = list_iter->second;
put(key, val);
return val;
}
void put(int key, int val)
{
auto mp_iter = mp.find(key);
if (mp_iter != mp.end())
{
auto list_iter = mp_iter->second;
mp.erase(key);
cache.erase(list_iter);
}
pair<int, int> p = make_pair(key, val);
cache.push_front(p);
mp[key] = cache.begin();
if (cache.size() > capacity)
{
pair<int, int> temp = cache.back();
cache.pop_back();
mp.erase(temp.first);
}
}
};
自定义实现Trie
- Trie树的结点不同于一般树的结点, 一般树的结点有一个确认值; 但是Trie树结点是一个指针数组, 数组的下标依次对应26个字母. 当对应下标的指针不为空时, 表明这个结点是该元素.
- 判断结尾时, 实际上是结尾字母所在结点的下一个结点为true
struct TrieNode
{
bool is_end;
TrieNode* links[26];
TrieNode()
{
is_end = false;
memset(links, 0, sizeof(links));
}
};
class Trie
{
public:
TrieNode* root;
Trie()
{
root = new TrieNode();
}
void insert(string word)
{
TrieNode* node = root;
for(char c : word)
{
if (node->links[c - 'a'] == NULL)
node->links[c - 'a'] = new TrieNode();
node = node->links[c - 'a'];
}
node->is_end = true;
}
bool search(string word)
{
TrieNode* node = root;
for(char c : word)
{
if (node->links[c - 'a'] == NULL)
return false;
node = node->links[c - 'a'];
}
return node->is_end;
}
bool startsWith(string prefix)
{
TrieNode* node = root;
for(char c : prefix)
{
if (node->links[c - 'a'] == NULL)
return false;
node = node->links[c - 'a'];
}
return true;
}
};