面试题3:数组中重复的数字
题目一:找出数组中重复的数字
题目:
在一个长度为n的数组里的所有数字都在0~n-1范围内。数组中某些数字是重复的,请找出数组中任意一个重复的数字。例如:输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复数字2或者3。
注意仔细读题,尤其是这些信息: 长度为n;数字范围是0~n-1(因此也一定是有重复数字的);找出任意一个。
动手写代码之前,问清楚面试官的需求,是求任意一个重复字符还是求出全部的重复字符。
分析:
- 可以用哈希表:从头到尾扫描数字,每扫描到一个数字就判断哈希表里是否已经包含了该数字,没有则加入哈希表,若有则表明哈希表里已经存在了该数字,就找到了这个重复的数字了。
- 数组重排法(本代码选择该方法):从头到尾扫描,比较该数字x是不是等于下标i,若是则接着再扫描下一个数字,若不是则再拿它和第i=x个数字作比较,若相等则找到了一个重复的数字;若不相等,则把第i个数字和第x个数字做交换,把x放到属于它的位置上,重复这个过程只到找出了重复字符。
这么说起来是有点乱啦,我们来画图举例吧:
文章图片
就相当于给数组排了个序,一旦找到后边数字有和前边已经排好顺序的数字有重复的,就结束了。
int main()
{
定义数组a[],数组长度n,当前数值x,结果值r;
输入n;
循环输入a[];
//特殊处理
输入空指针或者长度<=0的情况;
元素值超范围:小于0或者大于n-1的情况;
//操作(要不要定义成一个函数来被调用?)
for(int i=0;
i
代码: DuplicationInArray.cpp
#include
using namespace std;
int main()
{
int *num,n,x,r[1];
scanf("%d", &n);
num = (int*)malloc(sizeof(int)*n);
for(int i=0;
in-1)
return 1;
} //操作
int Temp, j=0, flag=1;
for(int i=0;
i
为什么这么写呢?
因为题目只要求找到任意一个重复的代码就行了,没有说找到全部的重复代码,所以只输出一个重复的值即可。
这次我命名了
DuplicationInArray.cpp
这样方便之后复习,也显得更专业点。源码也有:
//源代码
#include //定义一个布尔函数:有重复值返回true,否则返回false
//子函在前,主函里就不用声明了
bool duplicate(int numbers[], int length, int* duplication)
{
//无效值特殊处理
if(numbers == nullptr || length <= 0)
return false;
//非法输入特殊处理
for(int i = 0;
i < length;
++i)
{
if(numbers[i] < 0 || numbers[i] > length - 1)
return false;
}
//操作
for(int i = 0;
i < length;
++i)
{
//这里对于num[i]与i的值用了while判断
while(numbers[i] != i)
{
if(numbers[i] == numbers[numbers[i]])
{
*duplication = numbers[i];
return true;
//有重复值则返回true
}// 交换numbers[i]和numbers[numbers[i]]
int temp = numbers[i];
numbers[i] = numbers[temp];
numbers[temp] = temp;
}
}return false;
}// ====================测试代码====================
//这是啥?答:为了验证这个重复值是否是预期array里重复了的值
bool contains(int array[], int length, int number)
{
for(int i = 0;
i < length;
++i)
{
if(array[i] == number)
return true;
}return false;
}
//测试函数
//testName表测试几
//expected[]表期望结果值,也就是都有哪个值给重复了
//expectedExpected表期望结果值的sizeof
//validArgument表示有没有重复的函数
void test(char* testName, int numbers[], int lengthNumbers, int expected[], int expectedExpected, bool validArgument)
{
//输出一串字:表示测试几开始了
printf("%s begins: ", testName);
//调用这个布尔函数
int duplication;
bool validInput = duplicate(numbers, lengthNumbers, &duplication);
if(validArgument == validInput)//看输入是否有效
{
if(validArgument)
{
if(contains(expected, expectedExpected, duplication))//duplication是之前duplicate(numbers, lengthNumbers, &duplication);
里的
printf("Passed.\n");
else
printf("FAILED.\n");
}
else
printf("Passed.\n");
}
else
printf("FAILED.\n");
}// 重复的数字是数组中最小的数字
void test1()
{
int numbers[] = { 2, 1, 3, 1, 4 };
//发现了吗?空括号可以这么去定义
int duplications[] = { 1 };
test("Test1", numbers, sizeof(numbers) / sizeof(int), duplications, sizeof(duplications) / sizeof(int), true);
}// 重复的数字是数组中最大的数字
void test2()
{
int numbers[] = { 2, 4, 3, 1, 4 };
int duplications[] = { 4 };
test("Test2", numbers, sizeof(numbers) / sizeof(int), duplications, sizeof(duplications) / sizeof(int), true);
}// 数组中存在多个重复的数字
void test3()
{
int numbers[] = { 2, 4, 2, 1, 4 };
int duplications[] = { 2, 4 };
test("Test3", numbers, sizeof(numbers) / sizeof(int), duplications, sizeof(duplications) / sizeof(int), true);
}// 没有重复的数字
void test4()
{
int numbers[] = { 2, 1, 3, 0, 4 };
int duplications[] = { -1 };
// not in use in the test function
test("Test4", numbers, sizeof(numbers) / sizeof(int), duplications, sizeof(duplications) / sizeof(int), false);
}// 没有重复的数字
void test5()
{
int numbers[] = { 2, 1, 3, 5, 4 };
int duplications[] = { -1 };
// not in use in the test function
test("Test5", numbers, sizeof(numbers) / sizeof(int), duplications, sizeof(duplications) / sizeof(int), false);
}// 无效的输入
void test6()
{
int* numbers = nullptr;
int duplications[] = { -1 };
// not in use in the test function
test("Test6", numbers, 0, duplications, sizeof(duplications) / sizeof(int), false);
}void main()
{
test1();
test2();
test3();
test4();
test5();
test6();
}
题目二:不修改数组找出重复的数字
题目:
在一个长度为n+1的数组里所有的数字都在1~n范围内,所以至少有一个数是重复的,在不修改输入数组的情况下找出任意一个重复的数字。
分析:
- 思路1:新建一个数组array来复制输入的数组,对应的数放在该对应下标位置处,要是有重复的值立马就能找到。
- 思路2:利用二分查找法解决,这也是书中的方法。例如长为8的数组{2 3 5 4 3 2 6 7},数字范围在1-7内,中间数字4将其分为1-4和5-7。统计1-4在整个数组中出现的次数,若大于4,则必定有重复数。将1-4继续分成1-2和3-4,统计1-2在整个数组中的次数为2;再统计3-4在整个数组中出现的次数为3,所以3和4里必定有一个数是重复数,这样就找到了。
定义数组*num,长度n+1,新数组*Array;
输入长度n+1;
遍历输入数组num;
遍历输入数组Array[]并初始化为0;
//特殊处理
输入无效的情况;
数组里的值超范围的情况;
//处理
遍历num[]:
比较array[i]的值与num[i]的值,若不一样:
将num[i]放到Array[num[i]]处;
否则:
这个num[i]的值就是那个重复的值,赋值给结果r;
break;
输出r;
return 0;
思路1代码:
记得找到重复值就break跳出循环!由于这个问题导致我半天没解出来。
#include
using namespace std;
int main()
{
int *num,*Array;
int n, r=0;
scanf("%d", &n);
num = (int*)malloc(sizeof(int)*n);
Array = (int*)malloc(sizeof(int)*n);
for(int i=0;
in-1)
return 1;
}
//特殊处理
if(num==nullptr || n<=0)
return 1;
//操作
for(int i=0;
i
思路2伪代码:
定义数组*num,长度n+1;
输入长度n+1;
遍历输入数组num;
//特殊处理
输入无效的情况;
数组里的值超范围的情况;
//处理
int start = 1;
int end =n-1;
while(end>start)
{
定义中间值middle=((end-start)>>1)+start;
定义计数值count;
count = 中分后的这几个数在整个数组中出现次数;
若为一个数(输入可能全部为同样的值)时:若出现次数count>1,则该值就是重复值r=start;
若不为一个数时:
若出现次数count>数值范围:
遍历二分后的前半部分值:end=middle;
否则:
遍历二分后的后半部分值start=middle+1;
}
输出r;
return 0;
【剑指offer|键指offer——面试题3(数组中重复的数字(p37-43))】思路2代码:
(end-start)>>1
右移相当于(end-start)/2,左进位。#include
using namespace std;
int main()
{
int *num;
int n, r=0;
scanf("%d", &n);
num = (int*)malloc(sizeof(int)*n);
for(int i=0;
in-1)
return 1;
}
//特殊处理
if(num==nullptr || n<=0)
return 1;
//处理
int start = 1;
int end =n-1;
//迭代循环
while(end>=start)
{
middle = ((end-start)>>1)+start;
int count=0;
//统计这几个数在整个数组中出现的次数
for(int i=0;
i=start && num[i]<=end)
{
count++;
}
}
//一个数时:start == end就表明这是一个数
if(end == start)
{
if(count>1)
{
r=start;
break;
}
else
{
break;
}
//不为一个数时:end>start
else
{
if(count>(middle-start+1))
{
end = middle;
}
else
{
start = middle+1;
}
}
}
cout<
推荐阅读
- 个人日记|K8s中Pod生命周期和重启策略
- 学习分享|【C语言函数基础】
- C++|C++浇水装置问题
- 数据结构|C++技巧(用class类实现链表)
- C++|从零开始学C++之基本知识
- 步履拾级杂记|VS2019的各种使用问题及解决方法
- leetcode题解|leetcode#106. 从中序与后序遍历序列构造二叉树
- 动态规划|暴力递归经典问题
- 麦克算法|4指针与队列
- 遇见蓝桥遇见你|小唐开始刷蓝桥(一)2020年第十一届C/C++ B组第二场蓝桥杯省赛真题