【演算法】海量資料處理:有一千萬條簡訊,有重複,以文字形式儲存,一行一條,找出重複最少的前10條

pengfoo發表於2012-10-03

題目:有一千萬條簡訊,有重複,以文字形式儲存,一行一條,找出重複最少的前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;

}


 

相關文章