题目描述
设计一种结构,在该结构中有如下三个功能:
- insert(key):将某个key加入到该结构,做到不重复加入。
- delete(key):将原本在结构中的某个key移除。
- getRandom():等概率随机返回结构中的任何一个key。
要求:Insert、delete和 getRandom方法的时间复杂度都是 O(1)
。
思路
这个结构与哈希表的区别在于get的是key而不是value。
另外有一个getRandom函数,要严格等概率地返回结构中的任何一个key。
而只用一个哈希表并不能做到等概率地随机返回任何一个key。
因为哈希表中每个桶里挂着一些链,这些链的长度并不是严格相等的,只是差不多相等。在数量较少时甚至有些桶里链的长度是0。
于是可以设置两个map和一个size成员,map1对应keytoindex,map2对应indextokey
- 在
添加记录
时,如26个字母做记录按顺序进哈希表,最终哈希表的逻辑结构可能是这样的
表示A是第0个进来的,B是第1个进来的。。。Z是第25个进来的。
- 而在
删除记录
时,用随机洗牌的思想,每次都是将待删除的记录与最后一个记录进行交换,再删除最后一个记录并且将size减1。这样表就是连续的了。
如果不洗牌的话,在删除记录时中间会产生一个空洞,这样记录就不是连续的了。考虑极端的情况,当这个洞特别大,如果哈希表中没有这个记录,就会继续rand,那要rand很久,肯定不是O(1)的了。
测试结果及代码
#include <iostream>
#include <algorithm>
#include <string>
#include <math.h>
#include <map>
using namespace std;
template<typename T>
class RandomPool {
public:
RandomPool() : _size(0) { }
void add(T key) {
if (key2index.count(key))
return;
key2index[key] = _size;
index2key[_size] = key;
_size++;
}
T getRandom() {
int index = rand() % _size; // rand() -> [0, _size)
return index2key[index];
}
void Delete(T key) {
if (key2index.count(key)) {
int deleteIndex = key2index[key];
int lastIndex = _size - 1;
T lastKey = index2key[lastIndex];
key2index[lastKey] = deleteIndex;
index2key[deleteIndex] = lastKey;
key2index.erase(key);
index2key.erase(lastIndex);
_size--;
}
}
private:
map<T, int> key2index;
map<int, T> index2key;
int _size;
};
int main(int argc, char* argv[]){
RandomPool<string> pool;
pool.add("dzh");
pool.add("abc");
pool.add("123");
cout << pool.getRandom() << endl;
cout << pool.getRandom() << endl;
cout << pool.getRandom() << endl;
cout << pool.getRandom() << endl;
cout << pool.getRandom() << endl;
cout << pool.getRandom() << endl;
return 0;
}