文章目录
- 一、编程题
- 1.在长度 2N 的数组中找出重复 N 次的元素
- 2. 两个数组的交集
- 二、面试题
- 给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。【腾讯】
- (一)位图应用
- 1. 给定100亿个整数,设计算法找到只出现一次的整数?
- 2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
- 3. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
- (二)布隆过滤器
- 1. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出
- 2. 如何扩展BloomFilter使得它支持删除元素的操作
- (三)哈希切割
- 1. 给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?
- 2.与上题条件相同,如何找到top K的IP?
一、编程题
1.在长度 2N 的数组中找出重复 N 次的元素
链接
给你一个整数数组 nums ,该数组具有以下属性:
nums.length == 2 * n.
nums 包含 n + 1 个 不同的 元素
nums 中恰有一个元素重复 n 次
找出并返回重复了 n 次的那个元素
示例 1:
输入:nums = [1,2,3,3]
输出:3
示例 2:
输入:nums = [2,1,2,5,3,2]
输出:2
示例 3:
输入:nums = [5,1,5,2,5,3,5,4]
输出:5
class Solution {
public:
int repeatedNTimes(vector<int>& nums) {
unordered_map<int,int> countMap;
for(auto e : nums)
countMap[e]++;
for(auto& kv : countMap)
{
if(kv.second == nums.size()/2)
return kv.first;
}
return -1;
}
};
nbsp;
2. 两个数组的交集
链接
给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
// 用unordered_set对nums1中的元素去重
unordered_set<int> s1;
for (auto e : nums1)
s1.insert(e);
// 用unordered_set对nums2中的元素去重
unordered_set<int> s2;
for (auto e : nums2)
s2.insert(e);
// 遍历s1,如果s1中某个元素在s2中出现过,即为交集
vector<int> vRet;
for (auto e : s1)
{
if (s2.find(e) != s2.end())
vRet.push_back(e);
}
return vRet;
}
};
二、面试题
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。【腾讯】
这40亿个不重复的无符号整数占的空间:约等于15-16G
不能使用:
搜索树和哈希表都不太行(内存中存不下)
排序(O(NlogN)),利用二分查找: logN(数据太大,只能放在磁盘文件上,不好支持二分查找)
方法:
1.遍历,时间复杂度O(N)
2.位图解决——直接定址法 512MB
数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一
个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0
代表不存在。(比特位映射标记值)
效率很高
(一)位图应用
1. 给定100亿个整数,设计算法找到只出现一次的整数?
kv的统计次数搜索模型
0次 00
1次 01
2次 10
//位图是标准库中的一个容器
template<size_t N>
class twobitset
{
public:
void set(size_t x)
{
bool inset1 = _bs1.test(x);
bool inset2 = _bs2.test(x);
// 00
if (inset1 == false && inset2 == false)
{
// -> 01
_bs2.set(x);
}
else if (inset1 == false && inset2 == true)
{
// ->10
_bs1.set(x);
_bs2.reset(x);
}
}
void print_once_num()
{
for (size_t i = 0; i < N; ++i)
{
if (_bs1.test(i) == false && _bs2.test(i) == true)
{
cout << i << endl;
}
}
}
private:
bitset<N> _bs1;
bitset<N> _bs2;
};
void test_bit_set3()
{
int a[] = { 3, 4, 5, 2, 3, 4, 4, 4, 4, 12, 77, 65, 44, 4, 44, 99, 33, 33, 33, 6, 5, 34, 12 };
twobitset<100> bs;
for (auto e : a)
{
bs.set(e);
}
bs.print_once_num();
}
2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
3. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
类似题目1;
需要四种状态:
0次 00
1次 01
2次 10
3次 11
再添加一段代码:
else if (inset1 == true && inset2 == false)
{
// ->11
_bs1.set(x);
_bs2.set(x);
}
(二)布隆过滤器
1. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出
精确算法和近似算法
比如:网络请求、sql语法——本质:字符串
近似:一个文件放到布隆里,再从另外一个文件里看在不在。没有去重和有误判
精确:
哈希切分
假设每个query是30字节,100亿query需要多少空间?——3000亿字节,约等于300G
假设两个文件是A和B。一个是300G
A:依次读取文件A中的query,i = Hash(query)%1000,这个query就进去Ai小文件
B:依次读取文件B中的query,i = Hash(query)%1000,这个query就进去Bi小文件
放到内存的两个set中,编号相同的Ai和Bi小文件找交集即可
核心:因为哈希保证相同query一定进入相同编号的小文件
2. 如何扩展BloomFilter使得它支持删除元素的操作
多个位表示一个位置,做计数处理——这就可以支持删除,但是空间消耗更多,布隆的优势就削弱了。
(三)哈希切割
统计次数+topk
1. 给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?
读取每个ip,i = Hash(ip)%500,这个ip就进入第i个小文件
核心:相同的ip一定进入同一个小文件
依次使用map<string,int>对每个小文件统计次数
不是平均切割
2.与上题条件相同,如何找到top K的IP?
建一个k个值为<io,count>的小堆