一个非常常见的面试/笔试题是,一个大型的互联网公司,每天有很多用户浏览。如果想根据用户浏览的信息记录,来看用户最常选用的浏览路径是什么,怎么办? 假设用户数据已经收集到了一个文件里面,这个文iande格式是这样的:
(1)文件是文本文件,共若干行
(2)每一行的格式都相同,例如"用户ID字符串 网址". 在用户ID字符串和网址之间可能有多个空格或者tab。用户ID可能是数字或者非数字
(3)每个用户浏览都必须经过N层地址,例如<home>/<business>/<product>
那么如何统计? 显然要用到Hash表。用C++和Java分别实现:
(1)文件是文本文件,共若干行
(2)每一行的格式都相同,例如"用户ID字符串 网址". 在用户ID字符串和网址之间可能有多个空格或者tab。用户ID可能是数字或者非数字
(3)每个用户浏览都必须经过N层地址,例如<home>/<business>/<product>
那么如何统计? 显然要用到Hash表。用C++和Java分别实现:
- //
- // 1. 从文件读取内容并构造dp
- // 2. 遍历dp来构造每个用户id对应的url的地址,这是一个map<strId,urlParts>
- // 3. 当用户id的某个n部分地址构造完成的时候,写入url地址map<strId,count>
- // 3.1 如果set中不存在,创建一个
- // 3.2 如果set中已经存在了,对应计数+1
- // 4. 从set插入到一个排序的map当中
- // 5. 取出map的前N个元素就是所求
- //
- #include<algorithm>
- #include<deque>
- #include<fstream>
- #include<map>
- #include<string>
- #include<sstream>
- #include<unordered_map>
- #include<unordered_set>
- using namespace std;
- typedef pair<const string*,string*> line;
- deque<line> dp;
- struct urlParts{
- string* part1;
- string* part2;
- urlParts(string* p1,string* p2):
- part1(p1),
- part2(p2){}
- };
- void find_n_best(size_t nBest)//选择n个最多访问次数
- {
- struct strHash{
- size_t operator()(const string* str) const{
- return std::hash<string>()(*str);
- }
- };
- struct strComp{
- bool operator()(const string* p1,const string*p2) const{
- return *p1==*p2;
- }
- };
- unordered_map<string*,urlParts,strHash,strComp> idMap;
- unordered_map<string*,size_t,strHash,strComp> resultMap;
- for_each(dp.begin(),dp.end(),[&](const line& li){
- string* id=const_cast<string*>(li.first);
- auto it=idMap.find(id);
- if(it== idMap.end()){//没有找到,创建一个,作为1stPart
- idMap.insert(make_pair(id,urlParts(li.second,nullptr)));
- }else{//找到了,看看2ndPart有没有
- if(it->second.part2==nullptr){
- it->second.part2=li.second;
- }else{//已经有了3部分的url了
- string* part1=it->second.part1;
- string* part2=it->second.part2;
- string* part3=li.second;
- string* address=new string(*part1+'/'+*part2+'/'+*part3);
- auto fi=resultMap.find(address);
- if(fi==resultMap.end()){
- resultMap.insert(make_pair(address,1));
- }else{//计数已经存在了
- ++(fi->second);
- }
- }
- }
- });
- struct f{
- bool operator()(const size_t n1,const size_t n2) const
- {
- return n1>n2;
- }
- };
- map<size_t,string*,f> map2Sort;
- for_each(resultMap.begin(),resultMap.end(),
- [&](const pair<const string*,size_t>& p){
- const size_t n=p.second;
- string* ps=const_cast<string*>(p.first);
- map2Sort.insert(make_pair(n,ps));
- });
- for(auto it=map2Sort.begin();it!=map2Sort.end();++it){
- printf("count=%d,url=%s\n",it->first,it->second->c_str());
- if(!(--nBest))break;
- }
- for_each(resultMap.begin(),resultMap.end(),[](pair<const string*,size_t> p)
- {
- delete p.first;
- });
- }
- int main(void){
- ifstream fs(_T("d:\\url.txt"));
- if(!fs.is_open()){
- printf("open failed:%d\n",fs.fail());
- return 1;
- }
- int count=0;
- string sLine;
- stringstream ss;
- while(getline(fs,sLine)){
- string* pId=new string;
- string* pUrl=new string;
- ss.clear();
- ss<<sLine;
- ss>>*pId;
- ss>>*pUrl;
- if(pUrl->empty()){
- printf("%d\n",count);
- }
- dp.push_back(make_pair(pId,pUrl));
- ++count;
- }
- find_n_best(3);
- fs.close();
- for_each(dp.begin(),dp.end(),[](line li){
- delete li.first;
- delete li.second;
- });
- return 0;
- }
//
// 1. 从文件读取内容并构造dp
// 2. 遍历dp来构造每个用户id对应的url的地址,这是一个map<strId,urlParts>
// 3. 当用户id的某个n部分地址构造完成的时候,写入url地址map<strId,count>
// 3.1 如果set中不存在,创建一个
// 3.2 如果set中已经存在了,对应计数+1
// 4. 从set插入到一个排序的map当中
// 5. 取出map的前N个元素就是所求
//
#include<algorithm>
#include<deque>
#include<fstream>
#include<map>
#include<string>
#include<sstream>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef pair<const string*,string*> line;
deque<line> dp;
struct urlParts{
string* part1;
string* part2;
urlParts(string* p1,string* p2):
part1(p1),
part2(p2){}
};
void find_n_best(size_t nBest)//选择n个最多访问次数
{
struct strHash{
size_t operator()(const string* str) const{
return std::hash<string>()(*str);
}
};
struct strComp{
bool operator()(const string* p1,const string*p2) const{
return *p1==*p2;
}
};
unordered_map<string*,urlParts,strHash,strComp> idMap;
unordered_map<string*,size_t,strHash,strComp> resultMap;
for_each(dp.begin(),dp.end(),[&](const line& li){
string* id=const_cast<string*>(li.first);
auto it=idMap.find(id);
if(it== idMap.end()){//没有找到,创建一个,作为1stPart
idMap.insert(make_pair(id,urlParts(li.second,nullptr)));
}else{//找到了,看看2ndPart有没有
if(it->second.part2==nullptr){
it->second.part2=li.second;
}else{//已经有了3部分的url了
string* part1=it->second.part1;
string* part2=it->second.part2;
string* part3=li.second;
string* address=new string(*part1+'/'+*part2+'/'+*part3);
auto fi=resultMap.find(address);
if(fi==resultMap.end()){
resultMap.insert(make_pair(address,1));
}else{//计数已经存在了
++(fi->second);
}
}
}
});
struct f{
bool operator()(const size_t n1,const size_t n2) const
{
return n1>n2;
}
};
map<size_t,string*,f> map2Sort;
for_each(resultMap.begin(),resultMap.end(),
[&](const pair<const string*,size_t>& p){
const size_t n=p.second;
string* ps=const_cast<string*>(p.first);
map2Sort.insert(make_pair(n,ps));
});
for(auto it=map2Sort.begin();it!=map2Sort.end();++it){
printf("count=%d,url=%s\n",it->first,it->second->c_str());
if(!(--nBest))break;
}
for_each(resultMap.begin(),resultMap.end(),[](pair<const string*,size_t> p)
{
delete p.first;
});
}
int main(void){
ifstream fs(_T("d:\\url.txt"));
if(!fs.is_open()){
printf("open failed:%d\n",fs.fail());
return 1;
}
int count=0;
string sLine;
stringstream ss;
while(getline(fs,sLine)){
string* pId=new string;
string* pUrl=new string;
ss.clear();
ss<<sLine;
ss>>*pId;
ss>>*pUrl;
if(pUrl->empty()){
printf("%d\n",count);
}
dp.push_back(make_pair(pId,pUrl));
++count;
}
find_n_best(3);
fs.close();
for_each(dp.begin(),dp.end(),[](line li){
delete li.first;
delete li.second;
});
return 0;
}
OK, 上面这个C++的程序稍烦了一点,因为我先读完了文件再集中进行处理。我们也可以边读边处理。Java的ConcurrentHashMap提供了更加强大和方便的功能来做这样一件事情,构造和查找比C++更方便一点:
- import java.io.*;
- import java.util.*;
- import java.util.concurrent.*;
- public class BestURL{
- static class urlParts{
- String sPart1;
- String sPart2;
- }
- public static void main(String[] args) {
- ConcurrentHashMap<String,urlParts> dp=new ConcurrentHashMap<String,urlParts>();
- ConcurrentHashMap<String,Integer> result=new ConcurrentHashMap<String,Integer>();
- try{
- FileInputStream is=new FileInputStream("d:\\my.txt");
- Scanner scan=new Scanner(is);
- while(scan.hasNext()){
- String s=scan.nextLine();
- if(s.isEmpty())break;
- StringTokenizer t=new StringTokenizer(s," ");
- String key=t.nextToken(); //用户ID
- String value=t.nextToken();//用户访问的页面名称
- System.out.println(key+","+value);
- //对于每一行记录
- if(!dp.containsKey(key)){
- urlParts parts=new urlParts();
- parts.sPart1=value;
- dp.put(key, parts);
- }else{
- urlParts parts=dp.get(key);
- if(parts.sPart2==null){
- parts.sPart2=value;
- }else{
- //已经有了前面两部分的内容了
- String address=parts.sPart1+'/'+parts.sPart2+'/'+value;
- if(!result.containsKey(address)){
- result.put(address, 1);
- }else{
- Integer i=result.get(address);
- i+=1;
- result.put(address, i);
- }
- dp.remove(key);
- }
- }
- }
- }catch(FileNotFoundException e){
- e.printStackTrace();
- }
- class MyComp implements Comparator<Integer>{
- public int compare(Integer i1, Integer i2){
- int v1=i1.intValue();
- int v2=i2.intValue();
- return (v1>v2? -1:(v1==v2?0:1));
- }
- }
- TreeMap<Integer,String> sorted=new TreeMap<Integer,String>(new MyComp());
- Set<String> resultSet=result.keySet();
- for(Iterator<String> it=resultSet.iterator();it.hasNext();){
- String sKey=(String)it.next();
- Integer count=result.get(sKey);
- sorted.put(count, sKey);
- }//sorted是排好序的
- Set<Integer> sortedSet=sorted.keySet();
- for(Iterator<Integer> it=sortedSet.iterator();it.hasNext();){
- Integer key=(Integer)it.next();
- String add=sorted.get(key);
- System.out.println("count="+key.intValue()+",address="+add);
- }
- }
- }
- <SPAN style="FONT-FAMILY: Arial; BACKGROUND-COLOR: #ffffff"></SPAN>