1.主旨
- 考察数组本身的概念,以及在数组中实现对应数的查找。题目本身的含义很容易理解,所以对于这种简单的题目应该多从时间复杂度与空间复杂度这两个角度进行思考,这样才能体现出自己的思想优势。
2.考点
- 考点1:数组与指针的概念:数组占据着一块连续的内存储存数据,而指针是占着最小内存区间(通常为4B)的存储地址区间的变量。数组的名字为指向数组第一个元素的指针(比如a[2]就是从首元素向后移动三位),同时当数组进行参数传递时会退化成同类型的指针(即当单独使用数组名字时);
- 考点2:时间复杂度与空间复杂度:这道题的解法多种多样,无非就是这两者有所不同,如何根据面试官的出题来确定自己的思考方向,是其关键;
3.代码
- 本文使用两种常规方法和一种书上的方法,其中书中的不改变数组时选用的二分查找法本人个人认为无法查找出所有的重复数字,所以暂时未选择实现。
3.1 暴力枚举
- 简单无脑,看到时最容易想到的方法,两个for循环遍历,直到找到相同数。
- 时间复杂度O(n2),空间复杂度O(1),牛客网运行时间4ms,内存500K
bool duplicate1(int numbers[], int length, int* duplication)
{
for (int i = 0; i < length; i++)
{
for (int j = i; j < length; j++)
{
if (numbers[i] == numbers[j] && i != j)
{
*duplication = numbers[i];
return true;
}
}
}
return false;
}
3.2 哈希表(set)
- 没有必要自己去实现一个哈希表,C++标准库的map与set这两种数据结构足够使用。宗旨为利用set的单一值特性,在不断的insert中找到insert失败的数即可。同时此方法也可以轻易地拓展为找出所有相同的数。
- 时间复杂度O(n),空间复杂度O(n),牛客网运行时间4ms,内存350K—500K
bool duplicate2(int numbers[], int length, int* duplication)
{
set<int> number;
for (int i = 0; i < length; i++)
{
auto temp = number.insert(numbers[i]);
if (!temp.second)
{
*duplication = numbers[i];
return true;
}
}
return false;
}
3.3 下标对比法(书上的方法)
- 通过假设“对应的数值应该位于对应的下标”,遍历全数组,将当前数据不断地和其对应下标上的数据进行对比,从而找到重复值。
- 时间复杂度O(n),空间复杂度O(1),牛客网时间4ms,内存600K(从这里来看,反而没有3.2的哈希表好用哈哈)
bool duplicate2(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++)
{
while (numbers[i] != i)
{
if (numbers[i] == numbers[numbers[i]])
{
*duplication = numbers[i];
return true;
}
else
{
swap(numbers[i], numbers[numbers[i]]);
}
}
}
return false;
}
3.4 二分查找法(书上的方法)
- 基本思路为选取中心数,不断地将当前区间的数与中心数对比,如果中心数小于某段的数字数目,则表示那个区段存在重复数,而重复数的查找则就是在这其中进行不断地二分的过程。
- 时间复杂度O(nlogn),空间复杂度O(1),具体没实现所以不知道牛客网跑的效果