查找重复数字
在长度为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;
}