【算法】海量数据处理:有一千万条短信,有重复,以文本形式保存,一行一条,找出重复最少的前10条

题目:有一千万条短信,有重复,以文本形式保存,一行一条,找出重复最少的前10条

思路:通过哈希表去重并统计出重复次数后,通过堆调整找出重复次数最少的前10条

参考文章:http://gengning938.blog.163.com/blog/static/128225381201161994028740/,代码有改动。

关于从n(n很大)个数字中查找前k个最小的数的方法,用堆调整的方法,具体参见:

http://www.oschina.net/code/snippet_180974_6371和我之前的一篇博客:【数据结构】堆排序

下面给出经过改动的代码,编译是通过的。如果任何地方有什么纰漏之处,敬请指正。

#include<iostream>
#include<fstream>
#include<stdlib.h>
#include<malloc.h>
using namespace std;

const int ERROR=0;

struct LinkHash
{
	LinkHash *next;

	//用来装填短信息的内容
	char msg[10];
	int count;
};
struct SData
{
	char msg[10];
	int count;
};

//哈希函数,将一条短信息转成0~99的某值:[0,99] = f(短信息)
int Hash_Func(char const *p)
{
	int value=0;
	while(*p!='\0')
	{
		value=*p+value;
		p++;
	}
	return value%100;
}

class CHashTable
{
private:
	LinkHash *HashTable[100];
public:
	CHashTable();
	~CHashTable();

	void HashCollision(char const *p);
	void WriteToFile();

};
CHashTable::CHashTable()
{
	int i;
	for(i=0;i<100;i++)
	{
		HashTable[i]=(LinkHash*)malloc(sizeof(LinkHash));
		if(!HashTable[i])
			exit(ERROR);
		
		//初始化
		HashTable[i]->next=NULL;
		memset(HashTable[i]->msg, 0 , 10);
		HashTable[i]->count=0;

	}
}
CHashTable::~CHashTable()
{
	//释放开辟的内存空间(释放链接表)
	int i;
	LinkHash* p,*q;
	for(i=0; i<100; i++)
	{
		p = HashTable[i];
		while(p != NULL)
		{
			q = p;
			p = p->next;
			free(q);
		}	
	}

}

void CHashTable::HashCollision(char const *p)
{	
	int pos;
	LinkHash *head,*mobile,*newNode,*last;
	pos = Hash_Func(p);
	head = HashTable[pos];
	bool flag = false;
	if(head->count == 0)
	{
		strcpy(head->msg,p);
		head->count++;
	}
	else 
	{
		mobile = head;
		while(mobile!=NULL)
		{
			if(flag==false && strcmp(mobile->msg,p) == 0)
			{
				mobile->count++;
				flag = true;
				//break;//不用break是为了取得链表尾部指针
			}
			last = mobile;
			mobile = mobile->next;
		}
		if(flag == false)
		{
			newNode = (LinkHash *)malloc(sizeof(LinkHash));
			if(!newNode)
				exit(ERROR);
			newNode->next = NULL;
			strcpy(newNode->msg,p);
			newNode->count = 1;
			last->next = newNode;
		}
	}
}

//将原短信去重后统计出重复次数后写入result.txt文件中
void CHashTable::WriteToFile()
{
	int i;
	ofstream fout;
	LinkHash *p;
	fout.open("result.txt");//应写明正确路径
	
	for(i=0; i<100; i++)
	{
		p=HashTable[i];
		while(p)
		{
			fout<<p->msg<<" "<<p->count<<endl;
			p=p->next;
		}

	}
	fout.clear();
	fout.close();
}

//以下几个函数为完成查找n个数中(n很大)最小的前k个数
//最大堆调整
void swap(SData *a, SData *b)
{
	SData temp;
	temp = *a;
	*a = *b;
	*b = temp;
}

void HeapAdjust(SData *a,int i,int size)  //调整堆
{    
	int lchild=2*i;       //i的左孩子节点序号
	int rchild=2*i+1;     //i的右孩子节点序号
	int max=i;            //临时变量
	if(i<=size/2)          //如果i不是叶节点就不用进行调整
	{       
		if(lchild<=size&&a[lchild].count>a[max].count)
		{            
			max=lchild;
		}            
		if(rchild<=size&&a[rchild].count>a[max].count)
		{            
			max=rchild;
		}        
		if(max!=i)
		{            
			swap(&a[i],&a[max]);
			HeapAdjust(a,max,size);    //避免调整之后以max为父节点的子树不是堆
		}
	}
}

void BuildHeap(SData *a,int size)    //建立堆
{    
	int i;
	for(i=size/2;i>=1;i--)    //非叶节点最大序号值为size/2
	{        
		HeapAdjust(a,i,size);
	}
} 

int main()
{
	int i;
	const int k=10;
	char instr[10];
	SData Array[k+1];
	ifstream fin;
	ofstream fout;
	SData InData;

	/*fout.open("MessageData.txt");

	for(i=0;i<10000000;i++)//随机生成重复的短信,短信简化为三个字母
	{

	for(j=0;j<3;j++)
	str[j]='A'+rand()%10;
	str[3]='\0';
	fout<<str<<"\r\n";

	}
	fout.close();*/
	CHashTable cht=CHashTable();
	//MessageData.txt为存放海量信息的文本
	fin.open("MessageData.txt");
	if(fin.is_open())
	{
		while(fin>>instr)//将原短信文档读入并通过hash表处理
		{
			cht.HashCollision(instr);
		}
	}
	fin.close();
	fin.clear();

	//该函数将短信息以一定格式(如:我爱米老鼠 3453)写在result.txt中。
	cht.WriteToFile();

	//从result.txt中读取数据
	fin.open("result.txt");
	if(fin.is_open())
	{
		for(i=1;i<=k;i++)//先读取前k个
		{
			fin>>Array[i].msg>>Array[i].count;
			
		}


		cout<<"建堆:"<<endl;
		BuildHeap(Array,k);
		fin>>InData.msg>>InData.count; //继续读取文件中后面的数
		while(fin.fail()==false)//如果没有到文件尾
		{     

			if(InData.count<Array[1].count)//如果读取的数小于现有的堆的堆顶元素,则交换
			{
				Array[1]=InData;     
				HeapAdjust(Array,1,k);//调整为堆
			}
			fin>>InData.msg>>InData.count;
		}

	}
	//输出前k个数
	for(i=1;i<=k;i++)
		cout<<Array[i].msg<<" ------ "<<Array[i].count<<endl;

	return 0;

}


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值