[算法]在数组中查找重复数字-C#实现

查找重复数字

辅助数组方式

交换位置方式

二分查找方式

 

查找重复数字

在长度为N的数组里存储0~N-1间的数字,其中有些数字是重复的,但不知哪些数字重复和重复次数,如何找到任意一个重复的数字。

最简单的方式,可以依次遍历每个数字,然后查看后面是否有与此数字相同的数字;或者排序后,查看是否有相同的数字(相邻的数字相同)。但这些方式时间复杂度太高,后面给出一些其他实现方式,以下实现并没有完全处理边界,只给出主要流程代码;当找到时返回对应的数字,否则返回-1。

辅助数组方式

通过一个辅助数组,依次遍历数组中每个元素,存放到对应的位置,若对应位置已有数字则说明找到了重复的数字。此方式空间复杂度O(N)

public int FindWithArray(int[] arySource_)
{
    Debug.Assert(arySource_ != null);

    int[] aryDest = new int[arySource_.Length]; // All Zero
    aryDest[0] = -1; // Ensure differ with default
    for(int i=0;i<arySource_.Length; ++i)
    {
        int nNum = arySource_[i];
        if (aryDest[nNum] == nNum)
            return nNum;
        aryDest[nNum] = nNum;
    }

    return -1;
}

交换位置方式

通过重排数组的方式实现。从头到尾遍历数组中每个数字,比较下标i(设值为v)处的值是否等于i(即i=v),若相等则遍历下一个数字,否则:

  • 查看下标v处值,若是v,则找到了;
  • 否则交换v与i处的值,然后重复前面的操作;
    此方式虽然有两个循环,但是时间复杂度其实只有O(N)(每个数字最多交换两次,就放到了其自己的位置),空间复杂度只有O(1);但会改变数组自身。
void Swap(ref int nFirst_, ref int nSecond_)
{
    int nTmp = nFirst_;
    nFirst_ = nSecond_;
    nSecond_ = nTmp;
}

public int FindWithSwap(int[] arySource_)
{
    Debug.Assert(arySource_ != null);

    for(int i=0;i < arySource_.Length; ++i)
    {
        while (arySource_[i] != i)
        {
            int nNum = arySource_[i];
            if (arySource_[nNum] == nNum)
                return nNum;
            Swap(ref arySource_[i], ref arySource_[nNum]);
        }
    }

    return -1;
}

二分查找方式

借助二分查找的方式,统计数字数量;先把数字一分为二middle=(max-min)/2+max,统计[min,middle]间的数字数量:

  • 若数量大于middle-min+1,则肯定存在重复数字:若min=max,则查找到重复数字;否则继续查找[min,middle]范围内的数字;
  • 否则查找[middle,max]范围内的数字;
    此方式的时间复杂度为O(NlogN)(调用getBiCount最多logN次),空间复杂度O(1)
int getBiCount(int[] arySource_, int nLow_, int nHigh_)
{
    int nCount=0;
    for(int i=0; i<arySource_.Length; ++i)
    {
        if (arySource_[i] >= nLow_ && arySource_[i] <= nHigh_)
            ++nCount;
    }
    return nCount;
}

public int FindWithBiCount(int[] arySource_, int nMin, int nMax)
{
    Debug.Assert(arySource_ != null && arySource_.Length > 0);

    if (nMin > nMax)
    {
        Console.WriteLine("Min must less than Max");
        return -1;
    }
    if( nMax-nMin+1 > arySource_.Length)
    {
        Console.WriteLine("Digital interval must less than array-len");
        return -1;
    }
    while(nMin <= nMax)
    {
        int nMiddle = (nMax - nMin) / 2 + nMin;
        int nCount = getBiCount(arySource_, nMin, nMiddle);
        if(nMin == nMax)
        {
            if (nCount > 1) return nMin;
            break;
        }
        if (nCount > (nMiddle - nMin + 1))
            nMax = nMiddle;
        else
            nMin = nMiddle + 1;
    }

    return -1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值