找到重复的元素(Finding Repeated Elements)

leetcode Majority Element系列的解法讨论,参考文献

http://www.cs.utexas.edu/users/misra/scannedPdf.dir/FindRepeatedElements.pdf

问题描述:

在数组b[0..n-1]中找出所有出现频数多于n/k次的值。

基本思路:

遍历数组两次,第一次在数组中找到一个疑似众数的数值,第二次遍历计算该数值出现的频次,判定该值是否是众数。

算法:

基础算法:

i=0;
c=0;
do{
    if(v==b[i]){c+=2;i+=1;}
    else if(c==i){c+=2;i+=1;v=b[i];}
    else if(c!=i&&v!=b[i]) i+=1;
}while(i<n)
//only v may occur more than n/2 times in b[0:n-1]

该算法原理

  • 首先考虑循环中的第一个条件v==b[i]如果该条件被满足,则在b[i]中v出现的频次比b[i-1]多一次。所以我们将i增加1c增加2是为了让v的出现频次上限增加1vb[0:i-1]中最多出现c/2次)。
  • 再考虑第二个条件i==c时,i-c/2==i/2,此时没有哪个数字在b[0..i-1]中出现的次数多于i/2。所以在b[0…i]中出现次数多于i/2的只有可能是b[i]。
  • 让我们来看循环中的最后一个条件,当v!=b[i]的时候v[i]并不能拓展当前众数的频数,反而让当前众数在已经遍历的序列中的出现频数比例下降,于是我们保持c不变,i增加,直到ic相等的时候,如果仍然未出现一个b[i]与之前找到的众数相等,这个时候前面的序列中v已经不满足数量超过当前序列的长度的一半的条件了,它也已经不是众数了,所以我们以b[i]为新起点重新寻找众数。

第一个衍生算法:

我们想要将上面讨论的算法一般化。给定k和n,2<=k<=n,我们想要在数组b[0..n-1]中找到出现频次多于n/k的次的所有数值。上面的算法已经讨论了在k=2时的情况如何找出一个值,对于更加一般的情况2<=k<=n,在b中可能存在k-1个值出现的频次多于n/k次。对基础算法的一个简单的延伸如下:

首先我们对问题的解做一些约束:

v在b[0:i-1]中最多出现c/k次,

c>i,0<=i<=n,k整除c,

任何不是t中的pair的第一个component的数值在b[0:i-1]中最多出现s/k次,

0<=s<=i,k整除s。

上面提出的约束条件中关于集合t的很好满足,但是要找到其他数值的出现频次上限s/k却是一个难点。在我们之前对于k=2的情况的讨论中,我们约定其他的数值的频次上限是i-c/2,更一般的情况下,我们直接将k>=2情况下的约定拓展成作为t集合以外元素频次的上界。这种拓展一开始看起来很有道理,然而向t中添加一个新的pair(v,c)后会使得规定的上界减小太多。我们引入s是为了得到更加合理的频次上界。

//R是约束条件
R:v最多在b[0:n-1]中出现c/k次&&c>n&&k整除c&&序列b[0:n-1]中其他的元素出现次数最多n/k次
i=0, s=0, t={};
do{ 
        Let j be the index of a pair (v[j], c[j]) in t satisfying v[j]==b[i]
        if no such pair exists let j=0;
        if(j==0&& s+k<=i+1) {i+=1;s+=k;}
        else if(j==0&&s+k>i+1){i+=1;t.push_back{(b[i],s+k)};}
        else if(j!=0) {i+=1;c[j]+=k;}
        Delete all paris (v[j],c[j]) from t for which c[j]==i andif any are deleted,set s to i;
}while(i!=n)

第二个衍生算法 

第二个算法以来与一些极为简单的理论。考虑一个背包,背包中装了一些元素,元素中会有重复的数值,我们来考虑在背包中删除k个不同的元素,这个操作可以进行几次。一个k-reduced bag for bag B是从背包B衍生出来的,B一直执行上述操作知道不能再进行操作了。注意k-reduced bag 并不是唯一的,下面我们用背包{1,1,2,3,3}来举例说明。


假定B有N个元素,那么进行删除 k个不同元素的运算最多进行N/k次,最终集合中只会留下少于k个元素。只有出现在k-reduced bag for B中的元素才有可能在B中出现多于N/k次,而其他的元素都被删除了,不可能再出现,他们的出现次数最多是N/k次。

定理1  背包B中有N个物品,出现频次多于N/k的数值在背包B的k-reduced bag中。

将b[0:n-1]看作一个背包,我们可以利用上面的定理来提出一个算法。算法的结束条件是R:t是b的一个k-reduced bag。

在循环的过程中满足条件P: 0<=i<=n && t是b[0:i-1]的一个k-reduced bag&& d是t中的distinct的元素个数。

然后我们就可以写出下面的算法,这个算法不像之前提出的算法需要很多的解释。

i=0,d=0,t={};
do{
    if(b[i] not in t){
        t.push_back(b[i]);
        d+=1;
        if(d==k) Delete k distinct values from t and updat d;
        else if (d<k) break;
    }
    else if (b[i] in t) t.push_back(b[i]);
    i+=1;
}while(i!=n)

在之前的算法中,我们不能确定t的大小。而在这个算法中,t最多有k个distinct元素,接下来我们会解释如何实现背包t使得上述算法的时间复杂度为O(n*log k)。

实现背包t:

上述算法中的背包t最多有n个元素和d个distinct的元素,d<=k。在t和d上进行的操作如下:

1. t={}。 执行一次

2. 在t中查找b[i]。执行n次。

3. 向t插入一个元素。最多执行n次。

4. 从t中删除k个不同的元素,并更新d。 

我们使用AVL(平衡二叉树)来实现t,定义一个树T,有d个结点。每个结点都是一个pari(v[j],c[j])。

这样进行上面对t的操作的时间复杂度就能约束在O(n*log(k))内了。

上述的算法是一个决策树算法,达到了最优效果,其他的算法都是不能比拟的。


最后介绍一个原理和上面介绍的基础算法类似的一个经典的查找众数的算法:

摩尔众数投票算法(Boyer-Moore majority vote algorithm)

用法:找出一个序列中的众数。

时间复杂度:O(n)

空间复杂度:O(1)

最简单形式的该算法找出一个序列中的一个众数,该元素在序列中出现的次数大于序列长度的一半;如果该序列中没有这样的众数,该算法不会发现没有众数,而且仍然会输出序列中的某个元素。在第二种情况下,我们可以再次遍历一遍序列,确认找到的数是否真的是众数。

算法步骤如下:

1. 初始化一个元素m和一个计数器i,令i=0;

2. 遍历序列中的元素x。

    (a) 如果i=0,令m=x,i=1

    (b) 如果m=x,令i=i+1

    (c) 否则i=i-1;

3. 返回m



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Finding bugs(寻找错误)是指在软件开发过程中,为了保证软件的质量和稳定性,通过一系列的测试和调试过程,找出软件中存在的错误和缺陷,并进行修复的活动。 寻找错误是软件开发过程中必不可少的一步。在软件开发过程中,无论是编写代码、设计界面还是实施功能,都可能出现各种各样的错误。这些错误可能导致软件无法正常运行、功能异常或者性能低下。为了及时发现和修复这些错误,需要进行系统而全面的错误寻找工作。 寻找错误的方法和技巧有很多种。其中一种常用的方法是黑盒测试。黑盒测试是指在不了解软件内部结构和具体实现的情况下,通过输入一些指定的测试用例,观察软件的输出结果,并与预期结果进行对比,从而判断软件是否存在错误。另外一种方法是白盒测试。白盒测试是指在了解软件内部结构和具体实现的情况下,通过对代码进行逐行逐句的检查,发现其中潜在的错误。 除了以上的方法,还可以使用自动化的测试工具来辅助寻找错误。这些工具能够模拟用户的操作,快速地执行大量的测试用例,并生成详细的测试报告,帮助开发人员准确定位和修复错误。 在寻找错误的过程中,要保持耐心和专注。有时候错误可能隐藏得很深,需要仔细地分析和调试。同时,还要注重记录和总结错误,以便后续的修复工作。 总之,寻找错误是软件开发过程中不可或缺的一环。通过系统而全面的测试和调试工作,可以及时发现和修复软件中存在的错误和缺陷,提高软件的质量和可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值