汇总:剑指offer算法合集
题目
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:
2 <= n <= 100000
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof
解题思路
- 解题思路比较简单,直接遍历数组,找出两个相同的数即可,可以用一个数据集合暂存之前遍历过的值,例如HashMap
- 也可以直接用一个新的等长数组,初始化值全部为0,若遍历经过某个数,则置下标为这个数的位置上的值为1,表示这个数出现过。如果再次遍历到某个数,这个数也要放到这个下标中时,检测到这个下标位置的值为1,说明这个数出现过,即为重复数
- 根据上面数组的思想,可以考虑优化一下空间,既然额外的等长数组可以实现,那么在原数组上操作是否可以呢?答案是可以的,只需要在遍历的过程中,把当前的数与下标进行一一对应,例如遍历到下标为2的位置,但这个位置上的数为3,可以把这个3与下标为3的位置上的数做交换,多次进行此操作后,数组下标和数值就一一对应了
- 这时候在下一次操作中,可能出现几种情况:
- 当前位置的下标和值已经对应上了,则无需交换,继续遍历下一个位置
- 当前位置的下标和值对应不上,则根据值查找值应该对应的下标
- 如果找到的那个下标的位置上的数,恰好就是当前位置上的数,则说明这个数重复了,直接返回即
- 如果找到的那个下标的位置上的数,并不等于当前位置上的数,则用当前位置上的数和它进行位置交换,然后继续对当前位置上的数进行检查,看看属于这几种情况的哪一种
复杂度分析
只需要遍历一遍数组,虽然可能存在原地不动进行位置交换的情况,但那仅为O(1)
的时间复杂度,综合起来仍然属于O(n)
的时间复杂度范围内。而空间复杂度,利用数组本身进行记录,只需要其他常数级空间,空间复杂度为O(1)
代码实现
class Solution {
public int findRepeatNumber(int[] nums) {
int i = 0;
while (i < nums.length) {
//当前下标和数值对应,则遍历下一个
//这时候是数值自己等于自己的下标,无法得知是否与其他数重复
if (nums[i] == i) {
i++;
//如果当前数值与这个数作为下标的位置上的数不等,则交换这两个位置上的数
} else if (nums[i] != nums[nums[i]]) {
int t = nums[i];
nums[i] = nums[t];
} else {
//这两个数相等则说明重复,直接返回当前数
return nums[i];
}
}
return -1;
}
}