• 找top 10信息


    本文章内容来源于《程序猿面试宝典》。

    题目:

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


    解析:

    某些面试者想用数据库的办法来实现:首先将文本导入数据库,再利用select语句某些方法得出前10条短信。但实际上用数据库是满足不了5分钟解决这个条件的。这是由于1千万条短信即使1秒钟录入1万条(这已经算是非常快的数据录入了)5分钟才300万条。即使真的能在5分钟内录入完1千万条,也必须先建索引。不然sql语句5分钟内肯定得不出结果。但对1千万条记录建索引即使在5分钟之内都不可能完毕的。所以用数据库的办法是不行的。类似题目是怎样依据关键词搜索訪问最多的前10个站点。

    思路:

    hash表法:能够用哈希表的方法对1千万条分成若干组进行边扫描边建散列表。第一次扫描,取首字节,尾字节,中间随便两字节作为Hash Code,插入到hash table中。并记录其地址和信息长度和反复次数,1千万条信息,记录这几个信息还放得下。

    同Hash Code且等长就疑似同样,比較一下。同样记录仅仅加1次进hash table,但将反复次数加1。

    一次扫描以后,已经记录各自的反复次数。进行第二次hash table的处理。

    用线性时间选择可在O(n)的级别上完毕前10条的寻找。分组后每份中的top10必须保证各不同样,可hash来保证,也可直接按hash值的大小来分类。


    排序法:能够採用从小到大排序的方法,依据经验。除非是群发的过节短信,否则字数越少的短信出现反复的几率越高。建议从字数少的短信開始找起。比方一開始搜一个字的短信。找出反复出现的top10并分别记录出现次数,然后搜两个字的,依次类推。对于对同样字数的比較长的短信的搜索,除了hash之类的算法外。能够选择仅仅抽取头、中和尾等几个位置的字符进行粗判,由于此种推断方式是为了加快查找速度但未能得到真正期望的top10,因此须要做标记;如此搜索一遍后。能够从各次top10结果中找到备选的top10。假设这top10中有刚才做过标记的,则对其相应字数的全部短信进行精确搜索以找到真正的top10并再次比較。


    内存映射的方法:首先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]=='')
        {
              //此短信已完毕。计数器加1,并保存此短信内容
              pNode->ChildNodes[pText[nIndex]]->dwCount++;
              pNode->ChildNodes[pText[nIndex]]->pText=pText;
        }
        else   //if(pText[nText]!='')
        {
             //假设还未结束。就创建下一级节点
             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个节点。显示结果
    }


  • 相关阅读:
    mysql权限设置
    linux修改文件所属用户和组
    修改solaris 用户密码默认8位长度
    解决Solaris /home目录下无法创建目录问题
    MySQL修改root密码的多种方法
    solaris 安装jdk
    iPhone ZBar库 中文乱码解决方法重新编译libzbar.a
    ios中调用WCF
    Android自动在线升级(服务器为Tomcat)
    String字符串讲解
  • 原文地址:https://www.cnblogs.com/mthoutai/p/7221199.html
Copyright © 2020-2023  润新知