在数组中出现超过n / 3次的数字

时间:2013-02-19 10:52:09

标签: algorithm

我已经读过这个问题 Find the most common entry in an array

jon双向飞碟的回答只是让人心旷神情...... :)

现在我正在尝试解决这个问题,找到一个在数组中发生超过n / 3次的元素。

我很确定我们不能应用相同的方法,因为可能有2个这样的元素会发生超过n / 3次并且会给出错误的计数警报。所以我们可以调整jon周围的任何方式双向飞碟为此工作的答案..?

或者有任何解决方案将以线性时间运行吗?

5 个答案:

答案 0 :(得分:22)

Jan Dvorak's answer可能是最好的:

  • 从两个空候选插槽开始,两个计数器设置为0。
  • 每个项目:
    • 如果它等于任一候选者,则增加相应的计数
    • 如果有一个空槽(即一个计数为0的槽),则将其放入该槽并将计数设置为1
    • 否则将两个计数器减少1

最后,对阵列进行第二次传递以检查候选者是否确实具有所需的计数。您链接的问题不允许这样做,但我不知道如何针对此修改版本避免使用此问题。如果某个值超过n / 3次,那么它将位于一个插槽中,但您不知道它是哪一个。

如果问题的此修改版本保证两个值超过n/3个元素(通常k-1个值超过n/k那么我们就不需要第二遍了。但是当原始问题有k=2和1个保证多数时,我们无法知道我们是否应该"将其概括为保证1个这样的元素或保证k-1。保证越强,问题就越容易。

答案 1 :(得分:3)

您可以使用Selection algorithm查找n / 3位置和2n / 3中的数字。

n1=Selection(array[],n/3);
n2=Selection(array[],n2/3);
coun1=0;
coun2=0;

for(i=0;i<n;i++)
{
    if(array[i]==n1)
      count1++;
    if(array[i]==n2)
      count2++;
}
if(count1>n)
   print(n1);
else if(count2>n)
   print(n2);
else
   print("no found!");

答案 2 :(得分:2)

在第五行,if语句应该再检查一次:

if(n!=b && (cnt1 == 0 || n == a))

答案 3 :(得分:0)

如果数组中有n个元素,并且假设在最坏的情况下仅重复1个元素n / 3次,则选择一个数字而不是重复n / 3次的概率为( 2n / 3)/ n是1/3,因此,如果我们从大小为'n'的数组中随机选择N个元素,那么最终选择n / 3次重复数的概率将至少为1-(2 / 3)^ N。如果我们用99.99%的概率获得成功,则对于任何“ n”值,我们将获得N = 23。

因此,只需从列表中随机选择23个数字并计算它们的出现次数,如果我们得到的计数大于n / 3,我们将返回该数字,如果我们在随机检查了23个数字后没有得到任何解决方案,则返回- 1;

该算法本质上是O(n),因为值23不依赖于n(list的大小),因此在算法最坏的情况下,我们只能遍历数组23次。

面试位(C ++)上的接受代码:

  int n=A.size();
  int ans,flag=0;
  for(int i=0;i<23;i++)
  {

int index=rand()%n;
int elem=A[index];
int count=0;
for(int i=0;i<n;i++)
{
    if(A[i]==elem)
    count++;
}

if(count>n/3)
{
    flag=1;
    ans=elem;
}

if(flag==1)
break;
}

if(flag==1)
 return ans;
else return -1;
 }

答案 4 :(得分:0)

我使用以下Python解决方案来讨论算法的正确性:

class Solution:
    """
    @param: nums: a list of integers
    @return: The majority number that occurs more than 1/3
    """
    def majorityNumber(self, nums):
        if nums is None:
            return None
        if len(nums) == 0:
            return None

        num1 = None
        num2 = None
        count1 = 0
        count2 = 0

        # Loop 1
        for i, val in enumerate(nums):
            if count1 == 0:
                num1 = val
                count1 = 1
            elif val == num1:
                count1 += 1
            elif count2 == 0:
                num2 = val
                count2 = 1
            elif val == num2:
                count2 += 1
            else:
                count1 -= 1
                count2 -= 1


        count1 = 0
        count2 = 0

        for val in nums:
            if val == num1:
                count1 += 1
            elif val == num2:
                count2 += 1

        if count1 > count2:
            return num1

        return num2

首先,我们需要证明索赔A:

声明A :考虑一个列表C,其中包含多数数m,出现次数floor(n/3)次。从C中删除3个不同的数字后,我们得到C'mC'的多数数。

证明:使用R表示mC中的出现次数。我们有R > floor(n/3)R > floor(n/3) => R - 1 > floor(n/3) - 1 => R - 1 > floor((n-3)/3)。使用R'来表示mC'的出现次数。并使用n'表示C'的长度。由于删除了3个不同的数字,因此我们有R' >= R - 1n'=n-3很明显。我们可以有R' > floor(n'/3)中的R - 1 > floor((n-3)/3)。因此mC'的多数数。

现在让我们证明loop 1的正确性。将L定义为count1 * [num1] + count2 * [num2] + nums[i:]。使用m表示多数数。

不变

多数mL中。

初始化

第一次注册开始时,Lnums[0:]。因此,不变性是微不足道的。

维护

  1. if count1 == 0分支:在迭代之前,Lcount2 * [num2] + nums[i:]。迭代之后,L1 * [nums[i]] + count2 * [num2] + nums[i+1:]。换句话说,L不变。这样就保持不变。

  2. if val == num1分支:在迭代之前,Lcount1 * [nums[i]] + count2 * [num2] + nums[i:]。迭代之后,L(count1+1) * [num[i]] + count2 * [num2] + nums[i+1:]。换句话说,L不变。这样就保持不变。

  3. f count2 == 0分支:与条件1相似。
  4. elif val == num2分支:与条件2相似。
  5. else分支:在这种情况下,nums[i]num1num2彼此不同。迭代之后,L(count1-1) * [num1] + (count2-1) * [num2] + nums[i+1:]。换句话说,从count1 * [num1] + count2 * [num2] + nums[i:]中移出了三个不同的数字。根据声明A,我们知道mL的多数数。因此不变量得以保持。

终止

当循环终止时,nums[n:]为空。 Lcount1 * [num1] + count2 * [num2]

因此,当循环终止时,多数数为num1num2