2.3寻找发帖“水王”
问题:求在一个ID列表中出现次数超过一半的ID。
对于该问题:一个比较直接的想法就是直接统计。统计每个ID出现的次数。然后寻找该数组中最大的数值。从而找出它的ID。
统计需要N,并且空间上要2*N(而且这里必须保证ID不能是小数而且ID是连续的)。然后对于寻找最大元素。一种是分治。log2 N。一种是建个最大堆。(复杂度)
当然还有一种就是打擂台。N。
总体上需要N+N。或者N+log2 N
另外一种想法是排序后找中间的元素。因为大过一半。所以必然是该ID。利用快排 N*log2 N 很省事。
而且这个可以推广到那个扩展问题。当元素是大过1/4的时候。那我们只要找下标在3/4的时候即可。
在编程之美上面,有一个十分巧妙的想法。就是删除2个不同的ID。用来减小问题的规模。(减小问题规模这种思想是我们应该要具备的,这算是一种消元法吧?)
同时这个想法也是比较容易操作的。而且空间和时间上都有很大的提升,空间是常数的。时间是N的级别。
当初我不解并且觉得神奇之处在于不同的ID如何实现?一个直观的想法就是用一个 int 型数来保存之前遇到的ID。可是继续思考下去这并不方便。
那就是维护当前的id。如果下一个遇到的不是这个id就抵消。如果是的话就统计这个id已经出现了2次。最后id保存的一定是出现最多次数的ID。
//这里假设 ID 是 int 型的,就不用定义Type了, 编程之美上用Type。当然书上更好。 int Find(int ID,int N) { /* id 就是当前存储的id nTimes 代表这个id出现的次数 */ int id; int nTimes,i; nTimes = 0; for(i=0;i<N;i++) { if(nTimes==0) { nTimes = 1; id = ID[i]; } else { if(id == ID[i]) { nTimes ++; } else { nTimes --; } } } return id; }
反思:这一节的星数是一颗。但是我觉得这里面包含了缩小问题规模的重要思想。正如解决一个难题。我们可以把大化小。解小。而这种缩小问题规模的方法,在编程之美中是屡试不爽的。从求数组的最大值的那一篇章也可以了解到。从而引出了一个重要的算法思想。分治。