海量数据查询的探讨--一道风行公司笔试题

一 问题描述:风行网站的电影每天会被很多人浏览,有专门的日志文件记录着电影信息和被浏览的次数,现在设计一个算法,从这个日志文件里查询浏览次数最多的N部电影

 

二 设计思路:假设有一个日志文件已经记录了当天被浏览的电影信息和相应的浏览次数,至少有三种方法找到浏览次数最多的N部电影

方法一:利用二叉排序树

二叉排序树的特性是中序遍历可以得到一组升序的数据,那么我们只要按照逆向中序遍历,即右孩子->根->左孩子就可以得到降序的数据

方法二: 快速排序,请参看百度海量查找的我的博客

方法三:利用堆排序,可以建立大根堆,取出堆顶元素,然后在进行建堆

 

三 代码:本代码只针对第一种方法

/*
This is a free Program, You can modify or redistribute it under the terms of GNU
*Description:某个文件保存着大量电影信息,包括电影名和浏览次数
*从中选出浏览次数最多的N部电影
*Language: C++
*Development Environment: VC6.0
*Author: Wangzhicheng
*E-mail: 2363702560@qq.com
*Date: 2012/11/10
*/

#include <iostream>
#include <cstdlib>
#include <vector>
#include <string>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <ctime>
using namespace std;

/*
*电影类
*@name:电影名
*@visited:被观看次数
*/
class MovieRecord {
private:
	string name;          //电影名
	long visited;          //被观看次数
public:
	MovieRecord(string name="",long visited=0) {
		this->name=name;
		this->visited=visited;
	}
	MovieRecord(const MovieRecord &m) {
		this->name=m.getName();
		this->visited=m.getVisited();
	}
	MovieRecord & operator=(const MovieRecord &m) {
		this->name=m.getName();
		this->visited=m.getVisited();
		return *this;
	}
	bool operator<(const MovieRecord &m) {
		return this->visited<m.getVisited();
	}
	bool operator>=(const MovieRecord &m) {
		return !(*this<m);
	}
	bool operator==(const MovieRecord &m) {
		return this->name==m.getName() && this->visited==m.getVisited();
	}
	void setName(string name) {
		this->name=name;
	}
	string getName() const {
		return this->name;
	}
	void setVisited(long visited) {
		this->visited=visited;
	}
	long getVisited() const {
		return this->visited;
	}
	void show() const {
		cout<<"Movie Name:"<<name<<" "<<"Visited:"<<visited<<endl;
	}
	friend ostream & operator<<(ostream &os,const MovieRecord &m) {
		cout<<"Movie Name:"<<m.getName()<<" "<<"Visited:"<<m.getVisited()<<endl;
		return os;
	}
};

/*
*读取日志文件类
*@v:存放电影记录的向量
*@fs:日志文件对象
*此类完成的主要功能是将日志文件里的电影信息读入向量v
*/
class ReadLogFile {
private:
	vector<MovieRecord>v;
	fstream fs;
public:
	ReadLogFile(string name) {
		fs.open(name.c_str());
		if(!fs) {
			cerr<<"不能打开此日志文件!"<<endl;
			exit(1);
		}	
	}
	void Read() {
		string line;                      //获取文件每一行
		while(getline(fs,line)) {         //从文件读取每一行
			stringstream ss(line);        //字符串流ss
			string word;                  //word用于解析ss
			MovieRecord m;
			bool isName=true;             
			while(ss>>word) {
				if(isName) {
					m.setName(word);
					isName=false;
				}
				else {
					stringstream stream;
					long visited;
					stream<<word;         //将解析出的word推入字符串流中
					stream>>visited;      //从字符串流中抽取visited
					m.setVisited(visited);
				}
			}
			v.push_back(m);               //将新建的电影记录加入向量
		}
	}
	void GetV(vector<MovieRecord>&other) {
		vector<MovieRecord>::iterator it;
		other.clear();
		for(it=v.begin();it!=v.end();it++) {
			other.push_back(*it);
		}
	}
	void show()  {
		vector<MovieRecord>::iterator it;
		for(it=v.begin();it!=v.end();it++) {
			it->show();
		}
	}
	~ReadLogFile() {
		fs.clear();                        //清除文件流
		fs.close();                        //关闭文件
	}
};

/*-----------------方法一 二叉排序树-----------------------------------------------------------*/
template<class Type>                  //向前引用 
class BST;

/*
*二叉排序树结点类
*@data:结点的数据域
*@left:结点的左孩子指针
*@right:结点的右孩子指针
*/
template<class Type>
class BSTNode {
private:
	Type data;
	BSTNode *left;
	BSTNode *right;
public:
	friend class BST<Type>;
	BSTNode() {
	}
	BSTNode(Type data,BSTNode *left=0,BSTNode *right=0) {
		this->data=data;
		this->left=left;
		this->right=right;
	}
	BSTNode(const BSTNode &node) {
		this->data=node.data;
		this->left=node.left;
		this->right=node.right;
	}
	BSTNode & operator=(const BSTNode &node) {
		this->data=node.data;
		this->left=node.left;
		this->right=node.right;
		return *this;
	}
	void setData(Type& data) {
		this->data=data;
	}
	Type getData() const {
		return this->data;
	}
	void setLeft(BSTNode *left) {
		this->left=left;
	}
	BSTNode * getLeft() const {
		return this->left;
	}
	void setRight(BSTNode *right) {
		this->right=right;
	}
	BSTNode * getRight() const {
		return this->right;
	}
};

/*
*二叉排序树类
*@root:二叉排序树根指针
*@v:要建立二叉排序树所需数据的向量
*@TopN:存放观看次数最多的前N部的日志文件
*@N:前N部观看次数最多的电影
*/
template<class Type>
class BST {
private:
	BSTNode<Type> *root;        //二叉排序树的根指针
	vector<Type>v;              //存放数据域的向量
	vector<Type>TopN;    //存放观看次数最多的前N部的向量
	int N;                      //前N部观看次数最多的电影
    /*
	*向二叉排序树插入一个新的数据
	*@root:根指针
	*@data:一个新的数据
	*/
	void Insert(BSTNode<Type> *&root,const Type &data) {
		if(!root) {
			root=new BSTNode<Type>(data,0,0);
		}
		else if(root->getData()>=data) {
			Insert(root->left,data);
		}
		else {
			Insert(root->right,data);
		}
	}
	/*
	*中序遍历二叉排序树
	*@p:初始值是指向二叉排序树的根结点
	*/
	void VisitBST_In_order(BSTNode<Type> *p) {
		if(!p) return;
		VisitBST_In_order(p->getLeft());
		cout<<p->getData();
		VisitBST_In_order(p->getRight());
	}
	/*
	*按照逆向中序遍历寻找 即右孩子->根->左孩子
	*/
	void SearchTopN(BSTNode<Type> *p) { 
		static int n;
		if(!p) return;
		else {
			if(n<N) SearchTopN(p->getRight());
			if(++n<=N) TopN.push_back(p->getData());
			if(n<N) SearchTopN(p->getLeft());
		}
	}
public:
	/*
	*二叉排序树的构造函数
	*@input:输入的向量,保存着等待建立二叉排序树的数据
	*此函数将向量input的内容保存到向量v中,并且对v进行混洗,一定程度上避免产生单支树
	*/
	BST(vector<Type>&input) {
		v.clear();
		vector<Type>::iterator it;
		for(it=input.begin();it!=input.end();it++) {
			v.push_back(*it);
		}
		random_shuffle(v.begin(),v.end());                //将向量混洗,避免产生单支二叉排序树
		root=0;
		N=0;
	}
	/*
	*创建二叉排序树
	*/
	void Create() {
		vector<Type>::iterator it;
		for(it=v.begin();it!=v.end();it++) {
			Insert(root,*it);
		}
	}
	void show() {
		VisitBST_In_order(root);
	}
	/*
	*查询浏览次数最多的n部电影
	*/
	void SearchTopN(int n) {
		if(n<=0 || n>v.size()) {
			cerr<<"参数"<<n<<"应该大于0且小于"<<v.size()<<endl;
			exit(1);
		}
		N=n;
		SearchTopN(root);
	}
	/*
	*将向量TopN的内容保存到向量other中
	*/
	void GetV(vector<Type>&other) {
		vector<Type>::iterator it;
		other.clear();
		for(it=TopN.begin();it!=TopN.end();it++) {
			other.push_back(*it);
		}
	}
};

/*
*写日志文件类
*此类完成的主要功能是将向量TopN的内容写入文件fs中
*/
class WriteTopNToFile {
private:
	fstream fs;                     //日志文件,记录TopN部电影
	vector<MovieRecord>TopN;        //将向量TopN的内容写人文件  
public:
	/*
	*构造函数
	*@v:存放这浏览次数最多N部电影的记录
	*@filename:写入的文件名
	*/
	WriteTopNToFile(vector<MovieRecord>&v,const string filename) {
		vector<MovieRecord>::iterator it;
		TopN.clear();
		for(it=v.begin();it!=v.end();it++) {
			TopN.push_back(*it);
		}
		fs.open(filename.c_str());
		if(!fs) {
			cerr<<"不能打开此文件!"<<endl;
			exit(1);
		}
	}
	/*
	*写日志函数
	*/
	void WriteLog() {
		vector<MovieRecord>::iterator it;
		for(it=TopN.begin();it!=TopN.end();it++) {
			fs<<it->getName()<<" "<<it->getVisited()<<endl;
		}
	}
	~WriteTopNToFile() {
		fs.clear();
		fs.close();
	}
};

/*-----------------方法二 快速排序-----------------------------------------------------------*/
//代码省略

/*-----------------方法三 堆排序-----------------------------------------------------------*/
//代码省略

void main() {
	ReadLogFile rf("e:\\log.txt");
	rf.Read();
	rf.show();
	cout<<"----------------------"<<endl;
	vector<MovieRecord>other;
	rf.GetV(other);
	srand(unsigned(time(0)));
	BST<MovieRecord>bst(other);
	bst.Create();
	//bst.show();
	bst.SearchTopN(6);
	vector<MovieRecord>TopN;
	bst.GetV(TopN);
	WriteTopNToFile file(TopN,"e:\\TopN.txt");
	file.WriteLog();
}


 

四:测试

输入:

原始的日志文件:

A  1000
B  120
C  3100
D  1002
E  100010
F  2001
G  10001
H  900
I  12000
J  1210

一共十部电影信息

输出:

屏幕输出

 

 

将浏览次数最多的N部电影写入日志TopN.txt, 显示结果如下:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值