【转】已知一个数出现的次数严格超过了一半,请用O(n)的复杂度的算法找出这个数...

原文转自:http://blog.csdn.net/zhq651/article/details/7930284

方法1既然过半,那么用这个数与其他数配对的话,剩余的数字一定还是过半的这个数字。因此可以通过不断删除不同的2个数,直到没有不同的2个数,那么这个数就是要找的数。证明:最坏情况下,只有这个数同别的数配对,并且被删除,剩下的仍旧是这个数,因此得证。

  例:数组 a[5]={0,1,2,1,1};

     我们要查找的数字为1,操作步骤为:遍历整个数组,然后每次删除不同的两个数字,过程如下:

               0 1 2 1 1 =>2 1 1=>1

  具体实现:我们在考虑删除两个不同的数字的时候,实际上可以同过计数来实现,而不是物理上真正的删除。 在遍历数组的时候保存两个值:一个是数组中的一个数字,一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1。如果下一个数字和我们之前保存的数字不同,则次数减1。如果次数为零,我们需要保存下一个数字,并把次数设为1。由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为1时对应的数字。

  基于这个思路,我们不难写出如下代码:

#include<iostream>
#include<string>
using namespace std;

//全局变量,检查输入是否有效
bool invalidInput = false;

/************************************************************
/* 找出数组中出现次数超过数组长度一半的数字
/************************************************************/
int NumberAppearMoreThanHalf(int* numbers,unsigned int length)
{
    if(numbers == NULL || length <= 0)
    {
        invalidInput = true;
        return 0;
    }

    invalidInput = false;
    int key = numbers[0];
    unsigned int appearTimes = 1;
    for(int i = 1;i < length;++i)
    {
        if(appearTimes == 0)
        {
            key = numbers[i];
            appearTimes = 1;
        }

        if(numbers[i] == key)
            appearTimes++;
        else
            appearTimes--;
    }
    
    //检验输入的数组是含有满足条件的数字
    appearTimes = 0;
    for(i = 0; i < length; i++)
    {
        if(numbers[i] == key)
            appearTimes++;
    }

    if(appearTimes <= length / 2)
    {
        invalidInput = true;
        return 0;
    }
    
    return key;
}

int main()
{
    cout<<"Enter the length of your array:"<<endl;
    int arraylength = 0;
    cin>>arraylength;

    cout<<"Enter the elements of your array:"<<endl;
    int *array = new int[arraylength];
    for(int k = 0; k < arraylength;k++)
    {
        cin>>array[k];
    }

    cout<<"the number appear more than half length of your array is:"<<endl;
    cout<<NumberAppearMoreThanHalf(array,arraylength)<<endl;
    
    delete[] array;
    
    return 0;
}

  最近在学习Python,顺便用Python实现了下,编程比较粗糙,望谅解!

 1 def N_ON(L):
 2     print L
 3     i,j = 0,0
 4     while len(L) > 2:
 5         if L[i] != L[j+1]:
 6             del L[i]
 7             del L[j]
 8             i,j = 0,0
 9         else:
10             j = j + 1
11     print L[0]
12 
13 L = [2,1,3,1,4,1,1]
14 N_ON(L)
15 
16 #[2, 1, 3, 1, 4, 1, 1]
17 #1
18 #[Finished in 0.2s]

方法2是一种投票思路,定义2个变量;1个为候选人,另外1个为投票数。如果下一个人投票相同,则投票数加1,否则减1,当投票数小于0时,则更换为此时的候选人。循环一遍,剩下来的最后剩下来的就是这个数。证明:其中1个人投票过半,则剩余的人的票循环结束后,一定小于0,通过反正得证。

  转自:http://kenby.iteye.com/blog/1031114

  算法:充分利用出现次数超过一半这个特点,使用两个变量candidate和vote,分别代表候选人和票数,遍历数组。

     按如下方式投票和更换候选人:

        1)若当前数与候选人一样,则把候选人的票数加1

        2)若当前数与候选人不一样, 则把它的票数减1,如果减掉后票数小于0,则把候选人踢掉,用当前数作为新的候选人

        3)最后剩下的候选人就是出现次数超过一半的数。

  算法的正确性证明: 数组中,数值相同的数都会投赞成票,数值不同的都会投反对票,有一个数出现的次数超过一半,其它数得到的反对票必然大于一半,所以其它数中,任何一个得票都会小于0,遭到淘汰。剩下来的只能是超过一半的那个数。

#include <stdio.h>

int main(int argc, char **argv)
{
    int i, candidate, vote;
    int a[10]={1,2,3,1,2,1,1,6,1,1}; 

    candidate = 1<<31;
    vote = 0;
    for (i = 0; i < 10; i++) {
        if (a[i] != candidate) {
            if (vote == 0) {    /* 废掉candidate, 把a[i]作为新的候选人 */
                candidate = a[i];
                vote = 1;
            }
            else {            /* 候选人的票减1 */
                vote--;
            }
        }
        else {                /* 候选人的票加1 */
            vote++;
        }
    }
    // 最后剩下的候选人即为出现次数超过一半的数
    printf("candidate = %d, vote = %d\n", candidate, vote);

    return 0;
}

  同样给出Python代码:

 1 a = [1,2,3,1,2,1,1,6,1,1]
 2 can,vote = 1,0
 3 for i in xrange(0,len(a)):
 4     if a[i] != can:
 5         if vote == 0:
 6             can = a[i]
 7             vote = 1
 8         else:
 9             vote = vote - 1
10     else:
11         vote = vote + 1
12 print can

 

转载于:https://www.cnblogs.com/Sky-Yanjun/p/4971946.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值