程序员笔试面试题(1)

腾讯笔试题:

有一千万条有重复的短信,这些短信以一行一条有重复的文本文件形式保存,请用5分钟找出重复出现最多的前10条。

思路有如下几种:

  1. shell解决(由于效率不清楚,不做考虑)
    sort messages.txt  |uniq -c |sort -k1  |tail -10
  2. 用MD5处理一下,放入数据库中,然后
    再利用select语句某些方法得出前10条短信
    。(但是,我们需要注意的是实际上数据库是满足不了5分钟解决这个条件的,因为即使1s录入1万条5min才300万条,而且录入之后还必须建立索引,所以也不可行)
  3. 常规方法map先排序,在遍历一次,找出重复最多的前10条,排序的算法复杂度最低为nlgn.方法如下:建立一个红黑树a;遍历短信,对每条短信取MD5值,对每个MD5值在a中做操作:如果有值,这个key对应的值 就+1,否则就=1;遍历完后对红黑树取值最大的10个数,复杂度为10lg n
  4. hash_map

    可以设计一个hash_table,hash_map<string,int>,依次读取一千万条短信,加载到hash_table表中,并且统计重复的次数,与此同时维护一张最多10条的短信表,这样遍历一次就能找出最多的前10条,算法复杂度为

    O(n)。(

    hash_map的查找速度比map要快,因为hash_map的查找速度与数据量大小无关,属于常数级别。map的查找速度是log(n)级别。但是hash_map每次查找都需要执行hash函数,所以也比较耗时。而且,hash_map很多桶中可能都没有元素,所以内存利用率不高。

    )

    实现如下:

    #include<iostream>
    #include<map>
    #include<iterator>
    #include<stdio.h>
    using namespace std;//宏定义
    #include<ext/hash_map>
    #define HASH __gnu_cxx
    /*hash_map位于/usr/include/c++/4.0.0/ext 目录下在__gnu_cxx的名字空间中方便在mac或Linux下使用hash_map*/
    #define uint32_t unsigned int
    #define uint64_t unsigned long int//数据类型重定义
    struct StrHash
    {
            uint64_t operator()(const std::string& str) const
            {
               uint32_t b    = 378551;
               uint32_t a    = 63689;
               uint64_t hash = 0;
               for(size_t i = 0; i < str.size(); i++)
               {
                  hash = hash * a + str[i];
                  a    = a * b;
               }
               return hash;
            }
            uint64_t operator()(const std::string& str, uint32_t field) const
            {
               uint32_t b    = 378551;
               uint32_t a    = 63689;
               uint64_t hash = 0;
               for(size_t i = 0; i < str.size(); i++)
               {
                  hash = hash * a + str[i];
                  a    = a * b;
               }
               hash = (hash<<8)+field;
               return hash;
            }
    };
    struct NameNum{
            string name;
            int num;
            NameNum():num(0),name(""){}
    };
    int main()
    {
            HASH::hash_map< string, int, StrHash > names;
            HASH::hash_map< string, int, StrHash >::iterator it;
            NameNum namenum[10];
            string l = "";
            while(getline(cin, l))
            {
                    it = names.find(l);
                    if(it != names.end())
                    {
                            names[l] ++;
                    }
                    else
                    {
                            names[l] = 1;
                            names[l] = 1;
                    }
            }
            int i = 0;
            int max = 1;
            int min = 1;
            int minpos = 0;
            for(it = names.begin(); it != names.end(); ++ it)
            {
                    if(i < 10)
                    {
                            namenum[i].name = it->first;
                            namenum[i].num = it->second;
                            if(it->second > max)
                                    max = it->second;
                            else if(it->second < min)
                            {
                                    min = it->second;
                                    minpos = i;
                            }
                    }
                    else
                    {
                            if(it->second > min)
                            {
                                    namenum[minpos].name = it->first;
                                    namenum[minpos].num = it->second;
                                    int k = 1;
                                    min = namenum[0].num;
                                    minpos = 0;
                                    while(k < 10)
                                    {
                                            if(namenum[k].num < min)
                                            {
                                                    min = namenum[k].num;
                                                    minpos = k;
                                            }
                                            k ++;
                                    }
                            }
                    }
                    i++;
    
            }
            i = 0;
            cout << "maxlength (string,num): " << endl;
            while( i < 10)
            {
                    cout << "(" << namenum[i].name.c_str() << "," << namenum[i].num << ")" << endl;
                    i++;
            }
            return 0;
    }

    使用g++编译如下:
    g++ main.cpp -o main
    短信文本新建为:msg.txt
    注意:msg.txt中应该写入内容例如张三  张三 张三 张三  李四 李四 李四 李四。。。。
    运行:./main<msg.txt

  5. 可以采用内存映射的办法,首先1千万条短信按现在的短信长度将不会超过1G空间,使用内存映射文件比较合适。可以一次映射(当然如果更大的数据量的话,可以采用分段映射),由于不需要频繁使用文件I/O和频繁分配小内存,这将大大提高数据的加载速度。其次,对每条短信的第i(i从0到70)个字母按ASCII嘛进行分组,其实也就是创建树。i是树的深度,也是短信第i个字母。
        该问题主要是解决两方面的内容,一是内容加载,二是短信内容比较。采用文件内存映射技术可以解决内容加载的性能问题(不仅仅不需要调用文件I/O函数,而且也不需要每读出一条短信都分配一小块内存),而使用树技术可以有效减少比较的次数。
    struct TNode
    {
       BYTE *pText;
       //直接指向文件映射的内存地址
       DWORD dwCount;
       //计算器,记录此节点的相同短信数
       TNode *ChildNodes[256];
       //子节点数据,由于一个字母的ASCII值不可能超过256,所以子节点也不可能超过256
       TNode()
       {
           //初始化成员
       }
       ~TNode()
       {
            //释放资源
       }
    };
     
    //int nIndex是字母下标
    void CreateChilsNode(TNode *pNode,const BYTE* pText,int nIndex)
    {
        if(pNode->ChildNodes[pText[nIndex]]==NULL)
        {
            //如果不存在此子节点,就创建.TNode构造函数应该有初始化代码
            //为了处理方便,这里也可以在创建的同时把此节点加到一个数组中
            pNode->ChildNodes[pText[nIndex]]=new TNode;
        }
        if(pText[nIndex+1]=='\0')
        {
              //此短信已完成,计数器加1,并保存此短信内容
              pNode->ChildNodes[pText[nIndex]]->dwCount++;
              pNode->ChildNodes[pText[nIndex]]->pText=pText;
        }
        else   //if(pText[nText]!='\0')
        {
             //如果还未结束,就创建下一级节点
             CreateNode(pNode->ChildNodes[pText[nIndex]],pText,nText+1);
        }
    }
    
    //创建根节点,pTexts是短信数组,dwCount是短信数量(这里是1千万)
    void CreateRootNode(const BYTE **pTexts,DWOED dwCount)
    {
       TNode RootNode;
       for(DWORD dwIndex=0;dwIndex<dwCount;dwIndex++)
       {
            CreateNode(&RootN,pTexts[dwIndex],0);
       }
       //所有节点按dwCount的值进行排序
       //取前10个节点,显示结果
    }







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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值