[算法]查找数组中出现次数过半的数字

查找过半数字

基于Partition函数

计数方式

查找过半数字

数组中有某个数字出现的次数超过数组长度的一半,请找出这个数字。
最简单直接的方式是排序数组,然后中位数处的数字即为所要查找的数字,但是排序本身时间复杂度太高。

后面介绍一下其他更快的查找方式(设所有数字都非负,若未找到则返回-1)。

基于Partition函数

在快速排序算法中,需要先选择一个数字对数组进行分割(大的放在右侧,否则左侧);若分割后此数字切好在数组中间位置,则为查找的数字。

int toPartition(int[] aryAll_, int nStart, int nEnd)
{
    int nTmp = aryAll_[nStart];
    while (nStart < nEnd)
    {
        while (aryAll_[nEnd] > nTmp) --nEnd;
        if (nStart >= nEnd) break;
        aryAll_[nStart++] = aryAll_[nEnd];

        while (aryAll_[nStart] < nTmp && nStart < nEnd) ++nStart;
        if (nStart >= nEnd) break;
        aryAll_[nEnd--] = aryAll_[nStart];
    }

    aryAll_[nStart] = nTmp;
    return nStart;
}

public int FindViaPartition(int[] arySource)
{
    Debug.Assert(arySource != null && arySource.Length > 0);

    int nStart = 0;
    int nEnd = arySource.Length - 1;
    int nMiddle = arySource.Length / 2;
    int nIndex = toPartition(arySource, nStart, nEnd);
    while(nIndex != nMiddle)
    {
        if (nIndex < nMiddle)
            nStart = nIndex + 1;
        else // >
            nEnd = nIndex - 1;

        nIndex = toPartition(arySource, nStart, nEnd);
    }

    int nNum = arySource[nIndex];
    if (CheckIfMoreThanHalf(arySource, nNum))
        return nNum;

    return -1;
}

位于中间的数字不一定就是我们所要查找的数字(有可能数组中就没有过半的数字),为此需要统计此数字在数组出现的实际次数来判断:

bool CheckIfMoreThanHalf(int[] arySource, int nNumber)
{
    int nCount = 0;
    foreach(var n in arySource)
    {
        if (n == nNumber) nCount++;
    }

    return (nCount * 2) > arySource.Length;
}

计数方式

对数组中数字从前至后进行计数(记录当前计数的数字curNum与计数curCount),若有超过半数的数字存在,则其最后的计数肯定不为0(也有可能是较后面出现的数字,因此最后还需通过实际计数进行验证):

  • 若计数为0则,设当前数字为curNum,且计数为1;否则
  • 若当前数字与curNum相同,则增加计数;否则,则减少计数;
     
public int FindViaCount(int[] arySource)
{
    Debug.Assert(arySource != null && arySource.Length > 0);

    int nCount = 0;
    int nLast=0;
    foreach(var n in arySource)
    {
        if (nCount == 0)
        {
            nLast = n;
            nCount = 1;
        }
        else if(nLast == n)
        {
            ++nCount;
        }
        else
        {
            --nCount;
        }
    }

    if (CheckIfMoreThanHalf(arySource, nLast))
        return nLast;
    return -1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值