一道编程笔试题,无序整数数组第n小未出现的正偶数
有一个无序整数数组,要求找到第n小未出现的正偶数,实现函数
unsigned int getMinEven(int *a,int len,int k)
示例:
{-3,-2,2,4},4,1 输出:6
{2,4,7,8,5,10},6,2 输出:12
{2,2,2,2,2,2},6,2 输出6
要求时间复杂度为O(N),空间复杂度为O(1)
这道题来自CVTE的笔试是,改自IBM的笔试题 ,原题是要找出最小的未出现的正整数,不过思路大致相同
看到题后,会有先排序再查找的惯性思路,用O(nlgn)的快速排序、归并排序或者堆排序都不符合要求
排完之后,再对数组进行判断,复杂度也为O(nlgn) ,最后复杂度为O(nlgn) ,没能达到O(N)
最初的思路
于是再经过一番思考后,觉得可以假设整个数组已经排好,当出现一个不符合条件的数后,就对假定的正偶数进行调整,一直到整个数组扫描完,这样下来复杂度是O(N),于是开始写代码
int n_little(int *a, int length, int n)
{
if (a == NULL || length <= 0||n<0) return 0;
int target = n * 2; //如果整个数组是排好的正偶数,目标值就是n*2
int const_temp = target;
if (n > length) return target;
for (int i = 0; i < length; i++)
{
if (a[i]%2 == 0 &&a[i]>0&& a[i]<= const_temp) {
target += 2;
}
}
return target;
}
对案例进行想象输入后,发现最后一个数组是不通过的,相同的正偶数无法知道之前是否已经扫描过,这样子才发现这个思路还是有缺陷,想要进行修补,如扫描一次后再扫描看有没有重复的在支规定范围内的数,但这样子的查找可能会增加空间的使用,无法符合复杂度的要求
解决相同正偶数的问题
于是对上面的想法进行修正,假如在扫描的过程,能不耗费空间的情况下把相同的符合条件的正偶数进行统计。这样子一来,想要不耗费多余的空间的话,就只能在原数组上进行替换
当然,要替换的数肯定是用不到的,不符合要求的数。
于是,把思路理清之后就可以写代码了
int getMinEven(int arr[],int length,int k)
{
if (k == 0 || arr==NULL||length<=0)
return 0;
int left = 0; //left表示当前符合条件正偶数的位置
int r = length; //如果一个数字过大(不合法),就会被扔掉,r表示这个右边界
while (left < r)
{
if (arr[left] == (left + 1)*2)
{
left++;
}
else if (arr[left]> r*2 || arr[left] <=(left +1)*2 || arr[arr[left]/2-1 ] == arr[left])//不合法
{
arr[left] = arr[--r]; // --r注意,一旦不合法,这个右边界是会缩小的
}
else//合法但是没有在理想的位置上
{
//cout << "合法" << "\n";
int temp = arr[left];
arr[left] = arr[arr[left]/2 - 1];
arr[temp/2 - 1] = temp;
}
}
int target = 0;
for (int i = 0;i < length; i++ )
{
if (arr[i] != (i + 1) * 2)
{
--k;
}
if (k == 0)
{
target = (i + 1) * 2;
break;
}
}
if(k != 0)
{
target = length * 2 + k*2; //超出的部分
}
return target;
}
int main()
{
int a[6]= {2,2,2,2,2,2};
//int a[6] = { -2,2,2,4,6,9 };
int k = getMinEven(a, 6, 7);
cout << k;
system("Pause");
}
当然了,在这过程中,要注意到边界的问题
同时当得到一个符合条件正偶数数组后,k值的处理等问题
另这种做法会改变原来的数组,代码不保证完全正确,在自己的测试案例中是通过的,不保证案例覆盖得全