448. *Find All Numbers Disappeared in an Array

448. *Find All Numbers Disappeared in an Array

https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/description/

题目描述

Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once.

Find all the elements of [1, n] inclusive that do not appear in this array.

Could you do it without extra space and in O ( n ) O(n) O(n) runtime? You may assume the returned list does not count as extra space.

Example:

Input:
[4,3,2,7,8,2,3,1]

Output:
[5,6]

解题思路

思路: 看到类似这种数组中的元素的值限定在 1 ~ N 之间的, 可能就可以考虑使用将元素值作为索引的方法… 由于每个元素值的大小都是 1 <= a[i] <= N, 那么访问 a[a[i] - 1] 是没有问题的. 考虑上面这个数组:

# 假设数组的索引从 1 开始
index : 1   2   3   4   5   6   7   8
value : 4   3   2   7   8   2   3   1

现在从索引 1 开始, 如果依次将 a[abs(a[i])] 的值改为负数, 但如果 a[abs(a[i])] 已经是负数了, 那么就不用修改了.

# 假设数组的索引从 1 开始
index :  1    2    3     4   5   6     7    8
value : -4   -3   -2    -7   8   2    -3   -1

可以看到只有 {5, 6} 处的元素依然是正数, 那么消失的数字就是 {5, 6}. 由于下面的代码中索引从 0 开始, 所以最后要加 1.

核心思路就是: 对于 nums 中的每个元素, 将 nums[i] - 1 对应的那个元素设置为负值, 表示那个元素已经被访问过.

C++ 实现 1

通过将 nums[std::abs(nums[i]) - 1] > 0 的数值设置为负, 从而得到结果. 时间复杂度为 O ( N ) O(N) O(N).

class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) {
        for (int i = 0; i < nums.size(); ++i)
            if (nums[std::abs(nums[i]) - 1] > 0)
                nums[std::abs(nums[i]) - 1] *= -1;
        vector<int> res;
        for (int i = 0; i < nums.size(); ++i)
            if (nums[i] > 0)
                res.push_back(i + 1);
        return res;
    }
};

扩展阅读

leetcode 上的解答:

Java accepted simple solution

The basic idea is that we iterate through the input array and mark elements as negative using nums[nums[i] -1] = -nums[nums[i]-1]. In this way all the numbers that we have seen will be marked as negative. In the second iteration, if a value is not marked as negative, it implies we have never seen that index before, so just add it to the return list.

public List<Integer> findDisappearedNumbers(int[] nums) {
        List<Integer> ret = new ArrayList<Integer>();
        
        for(int i = 0; i < nums.length; i++) {
            int val = Math.abs(nums[i]) - 1;
            if(nums[val] > 0) {
                nums[val] = -nums[val];
            }
        }
        
        for(int i = 0; i < nums.length; i++) {
            if(nums[i] > 0) {
                ret.add(i+1);
            }
        }
        return ret;
    }

最为关键的是下面的解释:

大致意思可以理解为, 对于 nums 中的每个元素, 将 nums[i] - 1 对应的那个元素设置为负值, 表示那个元素已经被访问过, 之后判断 nums 中哪些值为正的, 如果为正, 说明对应的索引 i + 1 不存在在 nums 中.

https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/discuss/92956/Java-accepted-simple-solution/97460

A more detailed explanation for those who might still be confused:

This solution is using the relation between array index ([0, n-1]) and the given value range [1,n].

Each time when a new value X is read, it changes the corresponding Xth number (value at index X-1) into negative, indicating value X is read for the first time. For example. using the given test case [4,3,2,7,8,2,3,1], when it comes to i = 2 in the first loop, this solution marks the 2nd number (index = 1), indicating we’ve found number 2 for the first time.

When we encounter a redundant number Y, because we’ve marked the Yth position (index Y -1) when we saw Y for the first time, the if clause won’t let us flip it again. This leaves the already marked Yth number (number at index Y-1) negative. For example, in the given test case, when i = 5, val = |2| - 1 = 1, nums[1] = -3 < 0. No flip operation is needed because we’ve found value 2 before.

Looping through the 1st loop takes O ( n ) O(n) O(n) time, flipping signs won’t take extra space.

The second loop checks the signs of the values at indices. If the sign at index P is negative, it means value P + 1 is in the array. e.g. nums[0] = -4, so value 0+1 = 1 is in the array. If the value at index Q is positive, then value Q + 1 is not in the array. e.g. nums[4] = 8 > 0, value 4 + 1 = 5, we add 5 into the ret list.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值