题目:找到所有数组中消失的数字
    描述:给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。
    来源:力扣(LeetCode)448. 找到所有数组中消失的数字
    举例:

    输入:nums = [4,3,2,7,8,2,3,1] 输出:[5,6]

    输入:nums = [1,1] 输出:[2]

    提示:

    • n == nums.length
    • 1 <= n <= 105
    • 1 <= nums[i] <= n

    进阶:你能在不使用额外空间且时间复杂度为 O(n) 的情况下解决这个问题吗? 你可以假定返回的数组不算在额外空间内。
    题解:
    【方式一】使用一个临时数组进行统计
    例如:创建一个临时的数组,数组的长度与原数组长度一致,当原数组每出现一次,就在临时数组的【索引-1】处将值+1记为出现的次数,遍历原数组结束后,临时数组中仍为0的元素,其【索引+1】即为目标值
    原数组:[4,3,2,7,8,2,3,1]
    临时数组:[0,0,0,0,0,0,0,0]
    第1次遍历:[0,0,0,1,0,0,0,0]
    第2次遍历:[0,0,1,1,0,0,0,0]
    第3次遍历:[0,1,1,1,0,0,0,0]
    第4次遍历:[0,1,1,1,0,0,1,0]
    第5次遍历:[0,1,1,1,0,0,1,1]
    第6次遍历:[0,2,1,1,0,0,1,1]
    第7次遍历:[0,2,2,1,0,0,1,1]
    第8次遍历:[1,2,2,1,0,0,1,1]

    1. public List<Integer> findDisappearedNumbers(int[] nums) {
    2. int[] tempArrays = new int[nums.length];
    3. for (int i = 0; i < nums.length; i++) {
    4. //遍历数组,每出现一个元素就将这个元素值添加到临时数组的索引-1的位置
    5. tempArrays[nums[i]-1]++;
    6. }
    7. List<Integer> resultList = new ArrayList<>(nums.length/2);
    8. for (int i = 0; i < tempArrays.length; i++) {
    9. //遍历临时数组,如果当前位置的值<1,说明没有出现过
    10. if(tempArrays[i] < 1){
    11. resultList.add(i+1);
    12. }
    13. }
    14. return resultList;
    15. }

    【方法二】使用set集合辅助
    例如:创建一个set集合,遍历原数组,将原数组中元素添加到set集合中,最终声明一个次数为原数组大小的循环,依次判断当前值是否存在于set集合中,如果不存在即为目标值。

    1. public List<Integer> findDisappearedNumbers1(int[] nums){
    2. Set<Integer> numsSet = new HashSet<>();
    3. for (int i = 0; i < nums.length; i++) {
    4. //将数组中的每一位元素添加到set集合
    5. numsSet.add(nums[i]);
    6. }
    7. List<Integer> resultList = new ArrayList();
    8. for (int i = 1; i < nums.length+1; i++) {
    9. //循环从1开始,如果当前的i不在set集合中,就是目标值
    10. if(!numsSet.contains(i)){
    11. resultList.add(i);
    12. }
    13. }
    14. return resultList;
    15. }

    【方法三】将数组本身的值置为相反数,这种方式不会使用额外的空间
    例如:我们仔细观察这个原数组,发现可以使用他的索引对应位置的关系
    原数组:4,3,2,7,8,2,3,1
    索引值:0,1,2,3,4,5,6,7
    期望值:1,2,3,4,5,6,7,8
    我们遍历这个原数组,让原数组中的值的期望值,对应的索引位置的值,置为负数

    遍历元素 期望的索引 索引位置变化后 发生的改变
    4 3 [4, 3, 2, -7, 8, 2, 3, 1] 原数组中索引为3的值置为负数
    3 2 [4, 3, -2, -7, 8, 2, 3, 1] 原数组中索引为2的值置为负数
    2 1 [4, -3, -2, -7, 8, 2, 3, 1] 原数组中索引为1的值置为负数
    7 6 [4, -3, -2, -7, 8, 2, -3, 1] 原数组中索引为6的值置为负数
    8 7 [4, -3, -2, -7, 8, 2, -3, -1] 原数组中索引为7的值置为负数
    2 3 [4, -3, -2, -7, 8, 2, -3, -1] 原数组中索引为3的值已经为负,跳过
    3 2 [4, -3, -2, -7, 8, 2, -3, -1] 原数组中索引为2的值已经为负,跳过
    1 0 [-4, -3, -2, -7, 8, 2, -3, -1] 原数组中索引为0的值置为负数

    此时遍历完成后,原数组中仍然为正数的值对应的【索引+1】的值即为目标值
    注意:由于每次我们都改变了原数组,所以在每次取原数组中的值的时候,需要取绝对值,否则会出现索引越界

    1. public List<Integer> findDisappearedNumbers2(int[] nums){
    2. List<Integer> resultList = new ArrayList<>(nums.length/2);
    3. for (int i = 0; i < nums.length; i++) {
    4. //由于每次我们都改变了原数组,所以在每次取原数组中的值的时候,
    5. //需要取绝对值,否则会出现索引越界异常
    6. int num = Math.abs(nums[i]);
    7. int index = num - 1;
    8. if(nums[index] > 0){
    9. nums[index] = -nums[index];
    10. System.out.println(Arrays.toString(nums));
    11. }
    12. }
    13. for (int i = 0; i < nums.length; i++) {
    14. if(nums[i] > 0){
    15. resultList.add(i+1);
    16. }
    17. }
    18. return resultList;
    19. }