/*645、错误的集合*/
/*思路一:
 *  创建hash表,如果hash元素为1说明此元素重复,最后遍历一遍hash表,值为0说明缺少此元素
 * */
/*通过*/
public int[] findErrorNums(int[] nums) {
    int n = nums.length;
    int[] hash = new int[n + 1];
    int[] res = new int[2];
    for (int num : nums) {
        if (hash[num] == 1) res[0] = num;
        hash[num]++;
    }
    for (int i = 1; i < n + 1; i++) {
        if (hash[i] == 0) {
            res[1] = i;
            return res;
        }
    }
    return res;
}

/*697、数组的度*/
/*
 * 思路一:
 *   哈希表,需要用到map,键为nums中出现过的元素,值为一个三个元素空间的数组
 *   0索引为当前值出现过的次数,1、2索引分别为此元素第一次出现和最后一次出现的位置
 *   第一次遍历数组,如果map中没有此元素,添加进去,并且创建数组为1、i、i
 *   如果有此元素,将数组更新为+1、i、i+1
 *   第二次要遍历map,如果次数比最大次数小,跳过,如果等于,判断最短位置,如果大于,更新位置
 * */
/*注意度为两个位置索引的差+1*/
/*通过*/
public int findShortestSubArray(int[] nums) {
    HashMap<Integer, int[]> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        if (map.containsKey(nums[i])) {
            int[] arr = map.get(nums[i]);
            arr[0]++;
            arr[2] = i;
        } else {
            int[] arr = {1, i, i};
            map.put(nums[i], arr);
        }
    }
    int d = 0, size = 0;
    for (Map.Entry<Integer, int[]> entry : map.entrySet()) {
        int[] value = entry.getValue();
        if (value[0] == d) {
            size = Math.min(size, value[2] - value[1] + 1);
        } else if (value[0] > d) {
            d = value[0];
            size = value[2] - value[1] + 1;
        }
    }
    return size;
}

/*448、找到所有数组中消失的数字*/
/*
 * 思路一:
 *   哈希表,遍历数组,将当前元素索引位置的元素值改为负数,这样一来所有出现过的数字的对应位置上的值都为负数
 *   那么还是正数的位置对应索引是没有出现过的数
 * */
/*注意因为是1-n,所以对应索引要减一,*/
/*通过*/
public List<Integer> findDisappearedNumbers(int[] nums) {
    for (int i = 0; i < nums.length; i++) {
        nums[Math.abs(nums[i]) - 1] = -Math.abs(nums[Math.abs(nums[i]) - 1]);
    }
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < nums.length; i++) {
        if (nums[i] > 0) list.add(i + 1);
    }
    return list;
}

/*442、数组中重复的数据*/
/*
 * 思路一:
 *   遍历数组,得到当前元素对应索引位置的元素,判断其是否为负数,不是将其改为负数
 *   如果是说明此前已经有另一个一样的数字来修改过,那么将当前元素添加
 * */
/*注意,因为最多只可能出现两个相同的数字,所以取反可以直接乘以负一*/
/*通过*/
public List<Integer> findDuplicates(int[] nums) {
    List<Integer> res = new ArrayList<>();
    for (int i = 0; i < nums.length; i++) {
        if (nums[Math.abs(nums[i]) - 1] < 0) {
            res.add(Math.abs(nums[i]));
        } else {
            nums[Math.abs(nums[i]) - 1] *= -1;
        }
    }
    return res;
}

/*41、缺少的第一个正数*/
/*
 * 思路一:创建相同大小的hash表,遍历数组,将hash表对应位置修改为1
 *   然后遍历hash表,将第一个为0的索引返回(0除外)
 * */
/*通过,但是空间复杂度为o(n)*/
public int firstMissingPositive(int[] nums) {
    int n = nums.length;
    int[] hash = new int[n + 1];
    for (int num : nums) {
        if (num >= 0 && num <= n) {
            hash[num] = 1;
        }
    }
    for (int i = 1; i < n + 1; i++) {
        if (hash[i] == 0) return i;
    }
    return n + 1;
}

/*空间复杂度O(1)做法*/
/*思路:
 *   不难看出返回的结果肯定是介于1~n+1之间的
 *   将出现过的数字对应位置的元素在原数组上标记出来,这样就可以遍历原数组没有标记的就是最小正数
 *   关键点就是如何做标记,强调一点就是做标记后一定要还能看出原数字的值
 *   首先解决负数超出长度的数,负数是不做成分的,所以将所有负数都改为n+1
 *   然后是做标记,把对应位置的数改为负数,前提是原本为正数,这样只有有用并且没有出现过的数对应位置的数为正数
 *   如果都为正数说明都出现了,返回n+1
 * */
/*通过*/
public int firstMissingPositive2(int[] nums) {
    int n = nums.length;
    for (int i = 0; i < n; i++) {
        if (nums[i] <= 0) {
            nums[i] = n + 1;
        }
    }
    for (int i = 0; i < n; i++) {
        int num = Math.abs(nums[i]);
        if (num <= n) {
            nums[num - 1] = -Math.abs(nums[num - 1]);
        }
    }
    for (int i = 0; i < n; i++) {
        if (nums[i] > 0) return i + 1;
    }
    return n + 1;
}

/*274、H指数*/
/*题意分析:其实就是找出数组中x个数,这x个数每个都要大于等于x,并且剩下的数都要小于x
    比如第一个数要大于等于1,第二个数要大于等于2...
* */
/*思路一:
*   将数组排序,逆序遍历,判断当前数是否大于等于正序索引索引,是的话就继续遍历,否则退出
*   遍历的个数就是H指数
* */
/*通过*/
public int hIndex(int[] citations) {
    Arrays.sort(citations);
    int x = 1;
    for (int i = citations.length - 1; i >= 0; i--, x++) {
        if (citations[i] < x) {
            break;
        }
    }
    return x - 1;
}