By Jalan
知识工具需求
数据结构和算法
- 前缀和
- 二分
- hash映射
题解
第一次
思路
hash映射
设blackLength为黑名单长度。
我们把n分成两部分,前n - blackLength部分是白名单,后blackLength部分是黑名单。
然后我们将前半部分的黑名单映射到后半部分的白名单即可。
- 标记本来就在黑名单区域中的黑名单元素
- 将在白名单区域中的黑名单元素映射到黑名单区域中的白名单元素上
- 随机白名单区域,假如没有映射则说明是白名单元素,直接输出,有映射则说明是黑名单元素
编写用时
15分钟
代码
CPP
#include <bits/stdc++.h>
#include <cstdlib>
#include <unordered_map>
#include <vector>
using namespace std;
class Solution
{
public:
int whiteListSize;
unordered_map< int, int > hashMap;
Solution( int n, vector< int >&& blacklist )
{
int blackListSize = blacklist.size();
whiteListSize = n - blackListSize;
int hashIndex = n - blackListSize - 1;
//1 标记本来就在黑名单区域中的黑名单元素
for ( auto iter : blacklist ) {
if ( iter > whiteListSize - 1 ) {
hashMap[ iter ] = -1;
}
}
//2 将在白名单区域中的黑名单元素映射到黑名单区域中的白名单元素上
for ( auto iter : blacklist ) {
if ( iter <= whiteListSize - 1 ) {
++hashIndex;
while ( hashMap[ hashIndex ] == -1 ) {
++hashIndex;
}
hashMap[ iter ] = hashIndex;
}
}
}
int pick()
{
//3 随机白名单区域,假如没有映射则说明是白名单元素,直接输出,有映射则说明是黑名单元素
int random = rand() % ( whiteListSize );
if ( hashMap.count( random ) ) {
return hashMap[ random ];
} else {
return random;
}
}
};
/**
* Your Solution object will be instantiated and called as such:
* Solution* obj = new Solution(n, blacklist);
* int param_1 = obj->pick();
*/
int main( int argc, const char** argv )
{
Solution s( 1000000000, vector< int >{ 640145908 } );
for ( int i = 0; i < 10; i++ ) {
std::cout << s.pick() << std::endl;
}
return 0;
}
运行用时
第二次
思路
第一种方法时空效率不是很高.
(其实应该不错啊)
编写用时
20分钟,这个思路借鉴了 LeetCode:京城打工人 感谢
代码
CPP
class Solution
{
public:
using T = pair< int, int >; //左右端点[l,r] 从0开始
vector< T > m; // 存放各个区间
vector< int > s; // 存放前缀和, 即m各个区间长度的前缀和
Solution( int n, vector< int >&& blacklist )
{
sort( blacklist.begin(), blacklist.end() ); // 首先对黑名单区间排序
int st = 0; // st表示一个新区间的左端点, 默认为0
s.push_back( 0 );
for ( auto v : blacklist ) {
if ( v == st ) // 若该区间的待加入区间的左端点是黑名单的数则++
{
++st;
continue;
}
m.push_back( { st, v - 1 } ); // 加入一个新区间
s.push_back( s.back() + v - st ); // (v - 1) - st + 1 表示该新区间的长度 要表示的是[st,v-1]的长度,所以(v - 1) - st还需要+1
st = v + 1; //下一段的开始从本次的上界的右侧开始找.
}
// 最后一个区间的加入 st ~ n - 1, 但要注意判断是否可加入
if ( st != n ) {
m.push_back( { st, n - 1 } );
s.push_back( s.back() + n - st );
}
}
int pick()
{
int N = s.back();
int t = rand() % N + 1; // 总线段得到一个随机数, 因为区间长度至少为1, 所以需要+1, 从1开始
// 二分查找, 找 t 是落在那个区间上的
int l = 1, r = s.size() - 1;
while ( r > l ) {
int mid = ( l + r ) >> 1;
if ( s[ mid ] >= t )
r = mid;
else
l = mid + 1;
}
auto iter = m[ r - 1 ]; // 注意 r - 1, 因为m的区间是从0下标开始存储的
int n = iter.second - iter.first + 1; // 得到本次随机到区间的长度
return iter.first + rand() % n; // 该区间的(左端点数值 + 随机数) 即为最终答案
}
};
运行用时
结尾
看在我写了这么多注释的份上可以给我点个赞嘛,求求惹=]砰砰砰,给我加点写下去的油呀
@.@
也欢迎关注我的CSDN账号呀=]
**开心code每一天**