查重问题
查重:就是在一组海量数据中,查找重复的数据,一般的解题思路就是哈希表
哈希表
名称 | 特点 |
---|---|
unordered_set | 单重集合,只存放key,不允许key重复 |
unordered_multiset | 多重集合,只存放key,允许key重复 |
unordered_map | 单重映射表,存放[key, value]键值对,不允许key重复 |
unordered_multimap | 多重映射表,存放[key, value]键值对,允许key重复 |
示例问题:找第一个重复的数字
int main()
{
vector<int> vec;//将要查找的数据放在vec中
for (int i = 0; i < 200000; ++i)
{
vec.push_back(rand());
}
// 用哈希表解决查重,因为只查重,所以用无序集合解决该问题
unordered_set<int> hashSet;
for (int val : vec)
{
// 在哈希表中查找val
auto it = hashSet.find(val);
if (it != hashSet.end())//找到了
{
cout << *it << "是第一个重复的数据" << endl;
return;
}
else
{
// 没找到
hashSet.insert(val);
}
}
return 0;
}
统计数字及其出现的次数可以使用无序映射表
#include <iostream>
#include <unordered_map>
using namespace std;
int main()
{
vector<int> vec;
for (int i = 0; i < 200000; ++i)
{
vec.push_back(rand());
}
// 用无序映射表统计数字和数字出现的次数
unordered_map<int, int> hashMap;
for (int val : vec)
{
hashMap[val]++; // 可以直接记录数据并且更新数据出现的次数
}
// 打印统计出来的重复的数据
for (pair<int, int> value : hashMap)
{
if (value.second > 1)
{
cout << "key:" << value.first << " 重复次数:" << value.second << endl;
}
}
cout << endl;
return 0;
}
求top k
一、找前top k大的数据用小根堆,找前top k小的数据用大根堆
求vector容器中元素值最大的前10个数字
#include <iostream>
#include <queue>
#include <vector>
#include <functional>
using namespace std;
int main()
{
vector<int> vec;
for (int i = 0; i < 200000; ++i)
{
vec.push_back(rand() + i);
}
// 定义小根堆
priority_queue<int, vector<int>, greater<int>> minHeap;
// 先往小根堆放入10个元素
int k = 0;
for (; k < 10; ++k)
{
minHeap.push(vec[k]);
}
/*
遍历剩下的元素依次和堆顶元素进行比较,如果比堆顶元素大,
那么删除堆顶元素,把当前元素添加到小根堆中,元素遍历完成,
堆中剩下的10个元素,就是值最大的10个元素
*/
for (; k < vec.size(); ++k)
{
if (vec[k] > minHeap.top())
{
minHeap.pop();
minHeap.push(vec[k]);
}
}
// 打印结果
while (!minHeap.empty())
{
cout << minHeap.top() << " ";
minHeap.pop();
}
cout << endl;
return 0;
}
二、利用快排分割函数
partation函数
int partation(vector<int> &arr, int i, int j)
{
int k = arr[i];
while (i < j)
{
while (i < j && arr[j] >= k)
j--;
if (i < j)
arr[i++] = arr[j];
while (i < j && arr[i] < k)
i++;
if (i < j)
arr[j--] = arr[i];
}
arr[i] = k;
return i;
}
int findNoK(vector<int> &arr, int i, int j, int k)
{
int pos = partation(arr, i, j);
if (pos == k-1)//找到了
return arr[pos];
else if (pos < k-1)//在右边
return selectNoK(arr, pos + 1, j, k);
else//在左边
return selectNoK(arr, i, pos-1, k);
}
int main()
{
/*
求vector容器中元素第10小的元素值
*/
vector<int> vec;
for (int i = 0; i < 200000; ++i)
{
vec.push_back(rand() + i);
}
cout << findNoK(vec, 0, vec.size()-1, 10) << endl;
return 0;
}
对内存有限制的大数据处理
1.首先将所要计算的数据存进一个大文件中
2.由于内存限制,文件中的内容无法一次性加载进内存,需要将文件进行分割
3.将该大文件分割成每一个文件足够装载进内存的多个小文件
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
using namespace std;
// 大文件划分小文件(哈希映射)+ 哈希统计 + 小根堆(快排也可以达到同样的时间复杂度)
int main()
{
// 打开存储数据的原始文件
FILE *pf = fopen("data.dat", "rb");
if (pf == nullptr)
return 0;
// 这里由于原始数据量缩小,所以这里文件划分的个数也变小了,11个小文件
const int FILE_NO = 11;
FILE *pfile[FILE_NO] = { nullptr };
for (int i = 0; i < FILE_NO; ++i)
{
char filename[20];
sprintf(filename, "data%d.dat", i+1);
pfile[i] = fopen(filename, "wb+");
}
// 哈希映射,把大文件中的数据,映射到各个小文件当中
int data;
while (fread(&data, 4, 1, pf) > 0)
{
int findex = data % FILE_NO;//相同元素必然会在同一个文件中
fwrite(&data, 4, 1, pfile[findex]);
}
// 因为结果要记录数字和重复的次数,所以需要打包一个类类型
struct Node
{
Node(int v, int c) :val(v), count(c) {}
// 为什么要实现operator>,因为小根堆里面要比较Node对象的大小
bool operator>(const Node &src)const
{
return count > src.count;
}
int val; // 表示数字的值
int count; // 表示数字重复的次数
};
// 定义一个链式哈希表
unordered_map<int, int> numMap;
// 先定义一个小根堆
priority_queue<Node, vector<Node>, greater<Node>> minheap;
// 分段求解小文件的top 10大的数字,并求出最终结果
for (int i = 0; i < FILE_NO; ++i)
{
// 恢复小文件的文件指针到起始位置
fseek(pfile[i], 0, SEEK_SET);
while (fread(&data, 4, 1, pfile[i]) > 0)
{
numMap[data]++;
}
int k = 0;
auto it = numMap.begin();
// 如果堆是空的,先往堆放10个数据
if (minheap.empty())//其实就是当读第一个文件时才会进入该循环
{
// 先从map表中读10个数据到小根堆中,建立具有10个元素的小根堆,最小的元素在堆顶
for (; it != numMap.end() && k < 10; ++it, ++k)
{
minheap.push(Node(it->first, it->second));
}
}
// 把K+1到末尾的元素进行遍历,和堆顶元素比较
for (; it != numMap.end(); ++it)
{
// 如果map表中当前元素重复次数大于,堆顶元素的重复次数,则替换
if (it->second > minheap.top().count)
{
minheap.pop();
minheap.push(Node(it->first, it->second));
}
}
// 清空哈希表,进行下一个小文件的数据统计
numMap.clear();
}
// 堆中剩下的就是重复次数最大的前k个
while (!minheap.empty())
{
Node node = minheap.top();
cout << node.val << " : " << node.count << endl;
minheap.pop();
}
return 0;
}
来源:CSDN
作者:qq_43313035
链接:https://blog.csdn.net/qq_43313035/article/details/88855878