设计RandomPool结构一种结构,在该结构中有如下三个功能:
insert(key):将某个key加入到该结构,做到不重复加入;
delete(key):将原本在结构中的某个key移除;
getRandom():等概率随机返回结构中的任何一个key。
【要求】 Insert、delete和getRandom方法的时间复杂度都是O(1)。
由于要求添加、删除复杂度都为O(1),考虑使用Map。
Map是STL的一个关联容器,主要用于资料一对一映射的情況
#include
#include
#include
int main()
{
//构造方式key为string类型,value为int类型
map<string,int> mm;
mm[string("www")] = 2;
mm[string("eee")] = 4;
mm.insert(pair<string,int>("rrr",7));
mm[string("ttt")] = 0;
return 0;
}
假设本题的结构中key值存放a,b,c,d,e,使用map结构后,加入和删除均只需要O(1)的复杂度,但是对于等概率随机返回却不容易做到,考虑使用两个map结构,如下图,添加的时候两个map同时添加,且第二个map的key存放第一个map的value值,value存放第一个map的key值,这样的目的是可以通过map2的value值反求出map1的key值。
对于等概率随机返回问题,假设一共有26个数,value值存放0,1,2,3,4,5。。。25时,通过rand()函数随机出一个0-25之间的数,再通过map2找出对应的值,这样就可以随机返回map1的key值。此时value值视为桶号,挑选不同的桶把里面的东西即key倒出来,eg.0号桶是A。但是又有了新的问题出现,有时候删除过后,可能随机出的桶结果不存在,为了解决这种问题,我们需要保证桶号连续不间断。在删除时,把最大的桶的桶号更改为需要删除的桶号。例,假如删除B、1号桶,把当前最大的25号Z桶桶号更改为1,这样桶号是0-24依旧连续。
如上述分析,我们需要两个map来实现,在增加、删除时,两个map同增加同删除,在随机返回时,使用map2返回其value值达到目的。此外,还需要用于存放桶号的size。
class Pool
{
public:
Pool()
{
this->size = 0;
}
void map_insert(string word);
void map_delete(string word);
void getRandom();
private:
map<string,int> keyValueMap;
map<int,string> valuekeyMap;
int size ;//桶号
};
对于map1,要添加的key在map1的key中不存在时,调用map自带的insert函数添加,此时value值是当前的桶号size,而map2,key和value值与map1相反。map本身不允许有重复的key值,在这里使用find()函数进行查找需要添加的值,如果查找的迭代器为空则添加进去。
void Pool::map_insert(string word)
{
//find(key)存在返回该键的元素的迭代器;不存在返回m.end();
//if(this->keyValueMap.end() != this->keyValueMap.find(word))
map<string,int>::iterator pos = keyValueMap.find(word);
if(pos == keyValueMap.end())
{
keyValueMap.insert(pair<string,int>(word,size));
valuekeyMap.insert(pair<int,string>(size++,word));
}
}
map的删除可由迭代器删除,找到删除位置的迭代器m.erase(pos)
,和指定key值删除m.erase(key)
两种。
由于map的底层红黑树,map的value值可以进行修改,而key值不能修改。所以本来的想法是把桶最大的组覆盖掉删除的组的方法在c++行不通,考虑首先用迭代器记录map1删除的位置,接着分别记录桶最大的key值和删除的value值用作最后重新添加,再删除B,Z的四个部分,最后添加修改桶号后Z的两个部分。接下来需要定位四个需要删除的部分,对于map1中的B,题目中删除语句delete(word)
可以直接获得word值进行删除,而map2中的部分,通过迭代器记录map1的位置可找出map1中第二个参数value即1;而map1的Z通过map2查找最大桶号的value值,即map1的key值,最后是map2的25可以通过最大桶号直接获得。
注意,刚开始我想删除两个组,覆盖两个组,不能想当然使用反向迭代器即直接删除最后一个组,map1,map2横向不一定对齐。最终用最原始的删除四个添加两个代码最简洁。
void Pool::map_delete(string word)
{
//map1需要删除的位置用pos记录
map<string,int> ::iterator pos = keyValueMap.find(word);
if(pos != keyValueMap.end())//表示需要删除的存在
{
//通过map2取出桶号最大的key值,再取出需要保存的桶号
string key = valuekeyMap.find(size - 1)->second;
int value = pos->second;
//删除四个map部分,分别通过迭代器、按值查找获得
keyValueMap.erase(word);
keyValueMap.erase(key);
valuekeyMap.erase(value);
valuekeyMap.erase(--size);//桶号减一
//删除的恰好是桶最大,就不需要添加,直接返回
if(word == key)
return;
this->keyValueMap.insert(pair<string,int>(key,value));
this->valuekeyMap.insert(pair<int,string>(value,key));
}
}
通过rand()函数,返回桶的序号,再用map2查找第二个参数value即map1的key值
void Pool::getRandom()
{
srand((unsigned)time(NULL));
int num = rand() % size; //0 到 size-1
cout<<valuekeyMap.find(num)->second;
}
#include
#include
#include
#include
using namespace std;
class Pool
{
public:
Pool()
{
this->size = 0;
}
void map_insert(string word);
void map_delete(string word);
void getRandom();
private:
map<string,int> keyValueMap;
map<int,string> valuekeyMap;
int size ;//桶号
};
void Pool::getRandom()
{
srand((unsigned)time(NULL));
int num = rand() % size; //0 到 size-1
cout<<valuekeyMap.find(num)->second;
}
void Pool::map_delete(string word)
{
//map1需要删除的位置用pos记录
map<string,int> ::iterator pos = keyValueMap.find(word);
if(pos != keyValueMap.end())//表示需要删除的存在
{
//通过map2取出桶号最大的key值,再取出需要保存的桶号
string key = valuekeyMap.find(size - 1)->second;//second是value值,first是key值
int value = pos->second;
//删除四个map部分,分别通过迭代器、按值查找获得
keyValueMap.erase(word);
keyValueMap.erase(key);
valuekeyMap.erase(value);
valuekeyMap.erase(--size);//桶号减一
//删除的恰好是桶最大,就不需要添加,直接返回
if(word == key)
return;
keyValueMap.insert(pair<string,int>(key,value));
valuekeyMap.insert(pair<int,string>(value,key));
}
}
void Pool::map_insert(string word)
{
//find(key)存在返回该键的元素的迭代器;不存在返回m.end();
//if(keyValueMap.end() != this->keyValueMap.find(word))
map<string,int>::iterator pos = keyValueMap.find(word);
if(pos == this->keyValueMap.end())
{
keyValueMap.insert(pair<string,int>(word,size));
valuekeyMap.insert(pair<int,string>(size++,word));
}
}
int main()
{
Pool m1;
m1.map_insert("aaa");
m1.map_insert("bbb");
m1.map_insert("ccc");
m1.map_insert("ddd");
m1.map_insert("eee");
m1.map_insert("fff");
m1.map_insert("ggg");
m1.map_insert("hhh");
m1.map_insert("iii");
m1.map_insert("jjj");
m1.map_delete("aaa");
//测试添加key相同的元素
m1.map_insert("hhh");
m1.map_delete("ggg");
//测试删除size最大数
m1.map_insert("kkk");
m1.map_delete("kkk");
m1.getRandom();
//test 使用[]可以修改value值,
map<string,int> mm;
mm[string("www")] = 2;
mm[string("www")] = 4;
mm.insert(pair<string,int>("www",7));
mm[string("www")] = 7;
system("pause");
return 0;
}