左神算法基础class5—题目2设计RandomPool结构,可加入、删除、获得key,复杂度为O(1)

1.题目:设计RandomPool结构,可加入、删除、获得key,复杂度为O(1)

设计RandomPool结构一种结构,在该结构中有如下三个功能:
insert(key):将某个key加入到该结构,做到不重复加入;
delete(key):将原本在结构中的某个key移除;
getRandom():等概率随机返回结构中的任何一个key。
【要求】 Insert、delete和getRandom方法的时间复杂度都是O(1)。

2.Map的简单介绍

由于要求添加、删除复杂度都为O(1),考虑使用Map。
Map是STL的一个关联容器,主要用于资料一对一映射的情況

  • map中所有元素都是pair
  • pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
  • map<T1, T2> mp; //map默认构造函数:
#include<iostream>
#include<map>
#include<string>
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;
}

3.分析

假设本题的结构中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依旧连续。

在这里插入图片描述

(1)pool结构设计

如上述分析,我们需要两个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 ;//桶号

};

(2)添加设计

对于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));
	}
}

(3)删除设计

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));
	}
}

(4)等概率随机返回设计

通过rand()函数,返回桶的序号,再用map2查找第二个参数value即map1的key值

void Pool::getRandom()
{
	srand((unsigned)time(NULL));
	int num = rand() % size; //0 到 size-1
	cout<<valuekeyMap.find(num)->second;
}

4.完整代码

#include<iostream>
#include<map>
#include<string>
#include<time.h>
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;
}
发布了51 篇原创文章 · 获赞 1 · 访问量 1369

猜你喜欢

转载自blog.csdn.net/shi_xiao_xuan/article/details/104118493
今日推荐