zoukankan      html  css  js  c++  java
  • 主元素 寻找发帖水王

    寻找发帖“水王”

     

    题目是这样描述的:“水王”发帖的数目超过了所有帖子的一半,有各个帖子的作者ID,求这个水王的ID

    编程之美给出了两种巧妙的解法

    解法一:ID排序,那么ID列表中的N/2项即为要找的ID(还要排序,时间复杂度为O(NlogN))

    解法二:通过查找,每次从列表中除去两个不一样的ID,最后就可以得出这个ID,时间复杂度O(N)。写法上也有技巧,不必非要找到一个不一样的在继续下去,如果下一个一样,那么用一个变量记录这个次数,把次数+1,遇见不一样的-1。例如1,1,2,3,4 初始value =1,count =1,第二个1,value = 1,count =2,下一个2,value=1,count=1,下一个3,value=3,count=0,下一个4,value=4,count=1...一直到最后,看下value的值,就是所要找的ID。

    复制代码
    #include<iostream>
    using namespace std;
    /*
      Name: 寻找发帖“水王” 
      Copyright: 
      Author: 
      Date: 02/04/13 21:11
      Description: 每次删除两个不同的ID(不管是否包含“水王”的ID),那么“水王”的ID出现次数仍然超过总数的一半
      不断重复这个过程,时间复杂度O(n) 
    */
    
    int find(int ID[], int n)
    {
        int nTimes = 0, i, candidate;
         
        for(i = 0; i < n;i++)
        {
              if(nTimes == 0)
              {
                  candidate = ID[i];
                  nTimes = 1;
              }
              else
              {
                  if(candidate == ID[i])
                  {
                      nTimes++;
                  }
                  else
                  {
                      nTimes--;
                  }
              }
        }
        return candidate;
    }
    
    int main()  
    {  
        int arr[] = {1, 2, 3, 4, 2, 5, 2, 2, 3, 2, 5, 2};  
        printf("over harf id is %d
    ", find(arr, 12));  
        system("pause");
        return 0;  
    }  
    复制代码

    遍历一遍取值如下:

    i 0 1 2 3 4 5 6 7 8 9 10 11
    candidate 1   3   2   2          
    nTimes 1 0 1 0 1 0 1 7 1 2 1 2

    这个算法有个缺点,不能确切知道这个ID出现多少次,只能知道这个ID出现的次数大于N/2。如果想知道,最后还要遍历次,在海量数据的时候,多遍历一次的代价是要考虑的。。

    解法三:这个问题也可以用hash统计,在找出那个ID,时间复杂度为O(N),但是空间复杂度比解法二大些,最坏可能为O(N)而解法二为O(1)。

    为题扩张下~

    对于解法一,如果已知有两个发帖量在1/3以上的ID,可以在排序后,在N/3,2N/3的地方取一个数,在统计下这两个数出现的次数,既可以判断,时间复杂度O(NlogN)+O(N)还是O(NlogN)

    对于解法二,同样上述问题,那么value和count可以用一个二维数组来记录,同样时间复杂度为O(N)

    在对问题扩展下,对海量数据的时候~

    解法一就变成了“海量数据找出中位数的问题了”,而且不用全部的排序(不是海量数据的时候也可以按找中位数的方法)~这样,利用数的特性,每个整数都是用32位的二进制数表示的,按照最高几位的顺序分块,(分完后块是有大小顺序的)统计每块数的个数,就可以确定这个中位数在哪个块里面了,在对那个块这样运算,最终会找到这个中位数。

    对于解法二,也可以递推下,前提条件式必须是已知有M个发帖量在1/(M+1)个以上的ID~如果要保存的数目多的话,可以用hash-map代替数组

    对于解法三,这样的情况如果M个数还是没发读入内存的话,要先对ID求hash,在hash分组,在hash-map统计即可,如果可以读入内存则一次hash-map统计即可,在在hash-map中找出符合条件的值。与解法二相比,只是有部分空间复杂度上的降低。

    对于解法二的思想“减小问题规模,保持问题原有的性质”,对于原题目,性质就是水王的id 在小问题中仍然是超过总发帖量的一半。所以问题的关键就是解答,问题关键性质是什么,怎么减小问题的规模。对于问题的性质,那么就是具体问题具体分析了,而缩小规模,或者说是划分子问题,这个思想动态规划法,分治法都有用到,很大部分是由问题的性质决定的。

    其中最后,有一道扩展题,题目如下:
    随着Tango的发展,管理员发现,“超级水王”没有了。统计结果表明,有3个发帖很多的ID,他们的发帖数目都超过了帖子总数目N的1/4。你能从发帖ID列表中快速找出他们的ID吗?

    上题只需要一个结果,而现在需要3个结果,上题用到的nTimes,也应改为3个计数器。现在我们需要3个变量来记录当前遍历过的3个不同的ID,而nTimes的3个元素分别对应当前遍历过的3个ID出现的个数。如果遍历中有某个ID不同于这3个当前ID,我们就判断当前3个ID是否有某个的nTimes为0,如果有,那这个新遍历的ID就取而代之,并赋1为它的遍历数(即nTimes减1),如果当前3个ID的nTimes皆不为0,则3个ID的nTimes皆减去1。

    复制代码
    #include <iostream>
    
    using namespace std;
    
    int candidate[3];
    int count[3] = {0};
    
    int input[100];
    int num = 0;
    
    int main()
    {
        cout<<"please input"<<endl;
        int t;
        while(cin>>t)
        {
            if (t == -1)
                break;
            input[num++] = t;
        }
    
        bool flag = false;
    
        for (int i = 0;i < num;i++)
        {
            flag = false;
            for (int j = 0;j < 3;j++)
            {
                if (count[j] == 0)
                {
                    continue;
                }
                if (candidate[j] == input[i])
                {
                    count[j]++;
                    flag = true;
                }
            }
    
            if (flag == true)
            {
                continue;
            }
    
            for (int j = 0;j < 3;j++)
            {
                if (count[j] == 0)
                {
                    candidate[j] = input[i];
                    count[j]++;
                    flag = true;
                    break;
                }
            }
    
            if (flag == true)
            {
                continue;
            }
    
            for (int j = 0;j < 3;j++)
            {
                count[j]--;
            }
    
        }
    
        cout<<count[0]<<" "<<count[1]<<" "<<count[2]<<endl;
        cout<<candidate[0]<<" "<<candidate[1]<<" "<<candidate[2]<<endl;
    }
    复制代码
  • 相关阅读:
    Martix工作室考核题 —— 打印一个菱形
    Martix工作室考核题 —— 打印一个菱形
    Martix工作室考核题 —— 打印九九乘法表
    Martix工作室考核题 —— 打印九九乘法表
    Martix工作室考核题 —— 打印九九乘法表
    Martix工作室考核题 —— 201938 第三题
    Martix工作室考核题 —— 201938 第三题
    Martix工作室考核题 —— 201938 第三题
    Martix工作室考核题 —— 201938 第一题
    fiddler模拟发送post请求
  • 原文地址:https://www.cnblogs.com/bendantuohai/p/4483919.html
Copyright © 2011-2022 走看看