缺失的第一个正数
给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数。
示例 1:
输入: [1,2,0]
输出: 3
示例 2:
输入: [3,4,-1,1]
输出: 2
示例 3:
输入: [7,8,9,11,12]
输出: 1
方法一:哈希表
先将数组中出现的元素都加入到hash表中,在判断1~n+1是否在hash表中出现过,没有出现即为缺失的元素
public int firstMissingPositive(int[] nums) {
Set set = new HashSet<>();
for (int num : nums) {
set.add(num);
}
for (int i = 1; i <= nums.length + 1; i++) {
if (!set.contains(i)) {
return i;
}
}
return -1;//其实不可能达到
}
方法二:原地哈希
public int firstMissingPositive(int[] nums) {
boolean flag = false;
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 1) {
flag = true;//标记1出现过
}
if (nums[i] <= 0) {
nums[i] = 1;//将非正数转为1
}
}
if (!flag) {
return 1;//如果1没有出现过,缺失的就是1
}
for (int num : nums) {
num = Math.abs(num);
if (num <= nums.length && nums[num - 1] > 0) {// nums[num - 1] > 0保证只标记一次,防止负号不被改变
nums[num - 1] = -nums[num - 1];//将索引位置标记为出现过
}
}
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
return i + 1;
}
}
return nums.length + 1;//1~n都出现了
}
交换
/*
思路:使用座位交换法
缺失的第一个整数是 [1, len + 1] 之间,
遍历数组,将对应的数据填充到对应的位置上去,比如 1 就填充到下标0的位置, 2就填充到下表1的位置
如果填充过程中, nums[i] < 1 && nums[i] > len,那么直接舍弃
填充完成,再遍历一次数组,如果对应的 nums[i] != i + 1,那么这个 i + 1 就是缺失的第一个正数
比如 nums = [7, 8, 9, 10, 11], len = 5
我们发现数组中的元素都无法进行填充,直接舍弃跳过,
那么最终遍历数组的时候,我们发现 nums[0] != 0 + 1,即第一个缺失的是 1
比如 nums = [3, 1, 2], len = 3
填充过后,最终数组变成了 [1, 2, 3],每个元素都对应了自己的位置,那么第一个缺失的就是 len + 1 == 4
*/
public int firstMissingPositive(int[] nums) {
for (int i = 0; i < nums.length; i++) {
/*
1.只有在 nums[i] 是 [1, len] 之间的数,并且不在自己应该待的位置,nums[i] != i + 1 ,
2.并且它应该待的位置没有被同伴占有(即存在重复值占有)nums[nums[i] - 1] != nums[i] 的时候才进行交换
以上两点可以概括为nums[nums[i] - 1] != nums[i],因为它也代表了元素不在对应的位置
为什么使用 while ? 因为交换后,原本 i 位置的 nums[i] 已经交换到了别的地方,
交换后到这里的新值不一定是适合这个位置的,因此需要重新进行判断交换
如果使用 if,那么进行一次交换后,i +1 进入下一个循环,那么交换过来的新值就没有去找到它该有的位置
*/
while (nums[i] > 0 && nums[i] <= nums.length && nums[nums[i] - 1] != nums[i]) {
int temp = nums[i];
nums[i] = nums[temp - 1];
nums[temp - 1] = temp;
}
}
for (int i = 0; i < nums.length; i++) {
if (nums[i] != i + 1) {
return i + 1;
}
}
return nums.length + 1;
}
缺失数字
给定一个包含 0, 1, 2, ..., n
中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数。
示例 1:
输入: [3,0,1]
输出: 2
示例 2:
输入: [9,6,4,2,3,5,7,0,1]
输出: 8
方法一:哈希
public int missingNumber(int[] nums) {
Set set = new HashSet<>();
for (int num : nums) {
set.add(num);
}
for (int i = 0; i <= nums.length + 1; i++) {
if (!set.contains(i)) {
return i;
}
}
return -1;//其实不可能达到
}
方法二:排序
public int missingNumber(int[] nums) {
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (i != nums[i]) {
return i;
}
}
return nums.length;
}
方法三:异或
我们知道数组中有 n 个数,并且缺失的数在 [0..n] 中。因此我们可以先得到 [0..n] 的异或值,再将结果对数组中的每一个数进行一次异或运算。未缺失的数在 [0..n] 和数组中各出现一次,因此异或后得到 0。而缺失的数字只在 [0..n] 中出现了一次,在数组中没有出现,因此最终的异或结果即为这个缺失的数字。
public int missingNumber(int[] nums) {
int xor = nums.length;
for (int i = 0; i < nums.length; i++) {
xor ^= i ^ nums[i];
}
return xor;
}
方法四:和
用0~n之后减去数组元素之后得到缺失的数字
public int missingNumber(int[] nums) {
int n = nums.length;
int expectedSum = n * (n + 1) / 2;
int sum = 0;
for (int num : nums) {
sum += num;
}
return expectedSum - sum;
}
五:原地哈希
public int missingNumber(int[] nums) {
if (nums[0] != 0) {//确保0出现在第一位
for (int i = 0; i < nums.length; i++) {
if (nums[i] == 0) {
nums[i] = nums[0];
nums[0] = 0;
break;
}
}
}
for (int i = 0; i < nums.length; i++) {
int num = Math.abs(nums[i]);
if (num < nums.length) {
if (nums[num] > 0) {
nums[num] = -nums[num];
}
}
}
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
return i;
}
}
return nums.length;
}
后来想到的方法:交换
public int missingNumber(int[] nums) {
for (int i = 0; i < nums.length; i++) {
while (nums[i] < nums.length && nums[nums[i]] != nums[i]) {
int temp = nums[nums[i]];
nums[nums[i]] = nums[i];
nums[i] = temp;
}
}
for (int i = 0; i < nums.length; i++) {
if (nums[i] != i) {
return i;
}
}
return nums.length;
}
找到所有数组中消失的数字
给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。
示例:
输入:
[4,3,2,7,8,2,3,1]
输出:
[5,6]
方法一
先将应该出现的数字存入set中,再遍历数组,将出现的数字从set中移除,set中剩下的数字即为消失的数字
public List findDisappearedNumbers(int[] nums) {
Set set = new HashSet<>();
for (int i = 1; i <= nums.length; i++) {
set.add(i);
}
for (int num : nums) {
set.remove(num);
}
return new ArrayList<>(set);
}
方法二
遍历输入数组的每个元素一次
将把 |nums[i]|-1 索引位置的元素标记为负数。即 nums[∣nums[i]∣−1]×−1 。
然后遍历数组,若当前数组元素 nums[i] 为负数,说明我们在数组中存在数字 i+1
public List findDisappearedNumbers(int[] nums) {
List ans = new ArrayList<>();
for (int num : nums) {
int index = Math.abs(num) - 1;
if (nums[index] > 0) {
nums[index] *= -1;
}
}
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
ans.add(i + 1);
}
}
return ans;
}
交换的方法
public List findDisappearedNumbers(int[] nums) {
List res = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
while (nums[nums[i] - 1] != nums[i]) {
int temp = nums[i];
nums[i] = nums[nums[i] - 1];
nums[temp - 1] = temp;
}
}
for (int i = 0; i < nums.length; i++) {
if (nums[i] != i + 1) {
res.add(i + 1);
}
}
return res;
}
数组中重复的数字
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 :
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
方法一:Set
遍历数组,在遍历过程中将遇到的元素存起来,如果发现当前遇到的元素已经存在过就返回
public int findRepeatNumber(int[] nums) {
Set set = new HashSet();
for (int i = 0; i < nums.length; i++) {
if (set.contains(nums[i])) {
return nums[i];
}
set.add(nums[i]);
}
return -1;
}
方法二:排序
public int findRepeatNumber(int[] nums) {
Arrays.sort(nums);
for (int i = 1; i < nums.length; i++) {
if (nums[i] == nums[i - 1]) {
return nums[i];
}
}
return -1;
}
方法三:原地修改
由于数字都在0~n-1范围内,考虑num可以看做数组索引,遍历过程中将索引位置设为负数,如果下次索引位置为负数说明出现了相同的元素。由于存在0,需要特殊处理
public int findRepeatNumber(int[] nums) {
int zeroCount = 0;
for (int i = 0; i < nums.length; i++) {
int index = Math.abs(nums[i]);
if (nums[index] < 0) {
return index;
}
if (nums[i] == 0) {
zeroCount++;
}
if (zeroCount > 1) {
return 0;
}
nums[index] = -nums[index];
}
return -1;
}
原地交换
遍历中,第一次遇到数字 x 时,将其交换至索引 x 处;而当第二次遇到数字 x 时,一定有 nums[x] = nums[x]=x ,此时即可得到一组重复数字
public int findRepeatNumber(int[] nums) {
int i = 0;
while (i < nums.length) {
if (nums[i] == i) {
i++;
continue;
}
// 一定要在上一个if后,否则可能因为nums[i]=i而导致相等
if (nums[i] == nums[nums[i]]) {
return nums[i];
}
int temp = nums[i];
nums[i] = nums[temp];
nums[temp] = temp;
}
return -1;
}