题目要求

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) 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]

假设一个长度为n的整数数组,数组中的元素的值位于[1,n]区间中。问,该数组中有哪些[1,n]区间中的整数没有出现?

思路和代码

首先可以想到用另一个临时数组来记录每个元素出现的次数,则出现次数为零次的元素在数组中没有出现。代码如下:

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

但是这个实现违背了O(1)的空间复杂度(这里结果集不视为额外空间)。因此如何才能避免使用临时数组呢?其实我们可以利用原数组中元素相互调换的方式,将其转化为一个新的有序的数组。即从最左边开始,每遇到一个元素,就将其防止到元素的目标位置上,如在第0位上遇到元素i,则将位置i-1上的元素和位置0上的元素进行交换,并在此判断新的元素是否需要交换。如果当前元素无需进行交换,则指针右移一位。无需进行的场景是指当前元素已经出现在目标位置上了。

    public List<Integer> findDisappearedNumbers(int[] nums) {        int index = 0;        while(index < nums.length) {            if(nums[index] == nums[nums[index]-1]) {                nums[nums[index]-1] = nums[index];                index++;            }else{                int tmp = nums[index];                nums[index] = nums[tmp-1];                nums[tmp-1] = tmp;            }        }        List<Integer> result = new ArrayList<Integer>();        for(int i = 0 ; i < nums.length ; i++) {            if(nums[i] != i+1) {                result.add(i+1);            }        }        return result;    }