剑指 Offer 03. 数组中重复的数字
难度简单384
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。示例 1:
输入: [2, 3, 1, 0, 2, 5, 3] 输出:2 或 3
思路
应该首先沟通题目要求:
1、不要求空间,要求速度则用 hash 法(遍历数组,将元素存入 hashSet 中,遇到 Set 已经包含该元素的情况,则返回该重复值)
2、要求不占用额外空间,但可以修改数组本身,则先排序,然后遍历数组,判断数组元素与前一个元素重复即可;
3、要求不占用额外空间,但可以修改数组本身,还可以利用原地 hash(萝卜)法 、还可以利用相应坑位取负法
4、要求不占用额外空间,且不可以修改数组本身,使用二分法(不是先排序,再二分查找的那个——自己写的287),而是取中间值 m,遍历数组中小于 m 的元素的数量,如果数量大于 m 则说明重复值在(left,m)中,否则在(m,right)中
//萝卜坑法:一个萝卜一个坑,把萝卜放回它自己的坑里(使后来能够重复),如果发现他的坑里有了自己,也就是重复了,但是要对原位置进行重复判断(多次swap)
//bug1:for 内没用 while 循环,swap 一次之后,还要再判断当前坑位萝卜是不是属于这个坑
//bug2:只有当要换的元素不属于它的坑时,才进行替换(否则会不断争强该坑位)
public int findRepeatNumber(int[] nums) {
//原地 hash 法,时间 O(n)、空间O(1)
for(int i=0;i<nums.length;i++){
while(nums[i]!=i){
if(nums[nums[i]]!=nums[i]){
swap(nums,i,nums[i]);
}
else return nums[i];
}
}
return 0;
}
41. 缺失的第一个正数
难度困难1052
给你一个未排序的整数数组
nums
,请你找出其中没有出现的最小的正整数。进阶:你可以实现时间复杂度为
O(n)
并且只使用常数级别额外空间的解决方案吗?示例 1:
输入:nums = [1,2,0] 输出:3示例 2:
输入:nums = [3,4,-1,1] 输出:2示例 3:
输入:nums = [7,8,9,11,12] 输出:1
思路
萝卜坑,对于值不属于数组下标范围的元素,则不用管即可(因为要找的是没出现过的最小元素),第二次遍历,如果发现某元素不等于其下标加一,则说明该元素没出现过
class Solution {
public void swap(int[] nums,int i,int j){
int t=nums[i];
nums[i]=nums[j];
nums[j]=t;
}
//使用一个萝卜一个坑的解法
public int firstMissingPositive(int[] nums) {
for(int i=0;i<nums.length;i++){
while(nums[i]>0&&nums[i]<=nums.length&&nums[i]!=i+1&&nums[nums[i]-1]!=nums[i]){
//将 num[i]放在位置 num[i]上,并判断新的 num[i]
//nums[nums[i]-1]=nums[i];
swap(nums,i,nums[i]-1);
}
}
int i=0;
for(;i<nums.length;i++){
if(nums[i]!=i+1){
break;
}
}
return i+1;
}
}
448. 找到所有数组中消失的数字
难度简单713
给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。
示例:
输入: [4,3,2,7,8,2,3,1] 输出: [5,6]
思路
相应坑位取负法:数组元素均为正,且值在【1,n】之间,因此可利用长度为 n 的数组记录遍历过的元素是否存在,不利用额外空间的话,可在原数组进行操作,为了避免冲掉后续还未遍历的元素,通过对该元素取反的形式进行表示:对下标为 i 的元素取反,表示数组中包含 元素i+1,第二次遍历时,如果nums[i]为正,说明 i+1没出现过
class Solution {
//利用下标为 i 的元素的正负,作为对值为i的元素 是否存在的记录(存在则将响应位置元素置为负)
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> r=new ArrayList<>();
for(int i=0;i<nums.length;i++){
int num=Math.abs(nums[i]);
if(nums[num-1]>0){
nums[num-1]=-nums[num-1];
}
}
for(int i=0;i<nums.length;i++){
if(nums[i]>0){
r.add(i+1);
}
}
return r;
}
}
442. 数组中重复的数据
难度中等371
给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。
找到所有出现两次的元素。
你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗?
示例:
输入: [4,3,2,7,8,2,3,1] 输出: [2,3]
思路
数组元素均为正,且值在【1,n】之间,因此可利用长度为 n 的数组记录遍历过的元素是否存在,不利用额外空间的话,可在原数组进行操作,为了避免冲掉后续还未遍历的元素,通过对该元素取反的形式进行表示:对下标为 i 的元素取反,表示数组中已经包含 元素i+1,即该元素重复
public List<Integer> findDuplicates(int[] nums) {
//数组元素均为正,且值在【1,n】之间,因此可利用长度为 n 的数组记录遍历过的元素是否存在,不利用额外空间的话,可在原数组进行操作,为了避免冲掉后续还未遍历的元素,通过对该元素取反的形式进行表示:对下标为 i 的元素取反,表示数组中包含 元素i+1
List<Integer> result=new LinkedList<Integer>();
for(int i=0;i<nums.length;i++){
int num=Math.abs(nums[i]);
if(nums[num-1]<0){
result.add(num);
}else{
nums[num-1]=-nums[num-1];
}
}
return result;
}