海量数据查重和top k综合应用

问题描述

求数据的重复次数最大/最小的前K个/第K个元素

方法:哈希统计(map) + 堆/快排分割

求10万个数中重复数量最多的前10个数

#include <iostream>
#include <queue>
#include <unordered_map>
#include <functional>
using namespace std;

// 求海量数据中重复出现次数最多的前K个 / 第K个数
int main()
{
	// 求vec中出现次数最多的前10个数
	vector<int> vec;
	for (int i = 0; i < 100000; ++i)
	{
		vec.push_back(rand() % 100);
	}

	// 统计数字重复的次数
	unordered_map<int, int> numMap;
	for (int val : vec)
	{
		numMap[val]++;
	}

	// 定义小根堆
	using P = pair<int, int>;
	using FUNC = function<bool(P&, P&)>;
	using MinHeap = priority_queue<P, vector<P>, FUNC>;
	MinHeap minHeap([](auto& a, auto& b)->bool {
		return a.second > b.second; // 出现按次数比较,greater
		});

	// 初始化一个大小为10的小根堆
	int k = 0;
	auto it = numMap.begin();
	for (; it != numMap.end() && k < 10; ++it, ++k)
	{
		minHeap.push(*it);
	}

	// 剩余元素的出现次数依次和堆顶元素比较
	// 出现次数大于堆顶元素则出堆原来元素,入堆新元素
	for (; it != numMap.end(); ++it)
	{
		if (it->second > minHeap.top().second)
		{
			minHeap.pop();
			minHeap.push(*it);
		}
	}

	// 堆中元素就是重复次数最多的前K个元素
	while (!minHeap.empty())
	{
		const pair<int, int>& p = minHeap.top();
		cout << p.first << " : " << p.second << endl;
		minHeap.pop();
	}
	return 0;
}

大文件存储数据时对上题进行求解

分析

将大文件按内存限制分为小文件

将大文件里的数据做哈希映射到指定的小文件中;

每个文件依次进行哈希统计+小根堆比较操作,最后小根堆堆顶就是重复次数第K多的元素

执行流程:
  1. 生成大文件

  2. 生成小文件并将大文件数据散列到小文件中

  3. 定义小根堆

  4. 分段求解小文件的前K大的数字,堆始终不变

    1. 求每个文件中元素的重复次数(unordered_map)
    2. 堆空的时候才需要建堆(确定堆的固定大小)
    3. 剩余元素的出现次数依次和堆顶元素比较并进行出入堆操作
    4. 转a.到下一个小文件进行处理
完整代码
#include <iostream>
#include <queue>
#include <unordered_map>
#include <functional>
using namespace std;

int main()
{
	FILE* pf1 = fopen("data.dat", "wb");
	// 1.生成大文件
	for (int i = 0; i < 20000; ++i)
	{
		int data = rand();
		// 往文件pf1中写入&data指向的数据,每次写入4B,总共写1次
		fwrite(&data, 4, 1, pf1);
	}
	fclose(pf1);

	FILE* pf = fopen("data.dat", "rb");
	if (pf == nullptr)
		return 0;

	// 2.生成小文件并将大文件数据散列到小文件中
	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]);
	}

	// 统计数字重复的次数(某个小文件)
	unordered_map<int, int> numMap;

	// 3.定义小根堆
	using P = pair<int, int>;
	using FUNC = function<bool(P&, P&)>;
	using MinHeap = priority_queue<P, vector<P>, FUNC>;
	MinHeap minHeap([](auto& a, auto& b)->bool {
		return a.second > b.second; // 出现按次数比较
	});

	// 4.分段求解小文件的前K大的数字,堆始终不变
	for (int i = 0; i < FILE_NO; ++i)
	{
		// 恢复小文件的指针到起始位置
		fseek(pfile[i], 0, SEEK_SET);

		// 每次从pfile[i]中读取1个4B的数据
		// 4.1 求每个文件中元素的重复次数
		while (fread(&data, 4, 1, pfile[i]) > 0)
		{
			numMap[data]++; 
		}
		// 初始化一个大小为10的小根堆
		int k = 0;
		auto it = numMap.begin();
		// 只有堆空时需要建堆
		if (minHeap.empty())
		{
			for (; it != numMap.end() && k < 10; ++it, ++k)
			{
				minHeap.push(*it);
			}
		}
	
		// 4.2 剩余元素的出现次数依次和堆顶元素比较
		// 出现次数大于堆顶元素则出堆原来元素,入堆新元素
		for (; it != numMap.end(); ++it)
		{
			if (it->second > minHeap.top().second)
			{
				minHeap.pop();
				minHeap.push(*it);
			}
		}
		numMap.clear(); // 清空后进行下一个小文件的重复数据统计
	}

	// 堆中元素就是重复次数最多的前K个元素
	while (!minHeap.empty())
	{
		const pair<int, int>& p = minHeap.top();
		cout << p.first << " : " << p.second << endl;
		minHeap.pop();
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值