爱喝冬瓜汤的萝卜

导航

[算法]循环排序

这类题的特点是给定的数值和下表rank是类似的,其中可能会有一些差异.在设计算法的时候,可以将value值映射到rank上去.

其中,选择大于的值最好 比 rank的最大值+1,这样会避免导致 value % (nums.length)的时候会有同一个值.

 

题目范例:

268. 丢失的数字

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

 

示例 1:

输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。
示例 2:

输入:nums = [0,1]
输出:2
解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。
示例 3:

输入:nums = [9,6,4,2,3,5,7,0,1]
输出:8
解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。8 是丢失的数字,因为它没有出现在 nums 中。
示例 4:

输入:nums = [0]
输出:1
解释:n = 1,因为有 1 个数字,所以所有的数字都在范围 [0,1] 内。1 是丢失的数字,因为它没有出现在 nums 中。
 

提示:

n == nums.length
1 <= n <= 104
0 <= nums[i] <= n
nums 中的所有数字都 独一无二
 

进阶:你能否实现线性时间复杂度、仅使用额外常数空间的算法解决此问题?

 

题解:

       public int missingNumber(int[] nums) {

        int len = nums.length + 1;
        for (int i = 0; i < nums.length; i++) {
            int targetRank = nums[i] % len;
            if (targetRank < nums.length) {
                nums[targetRank] += len;
            }
        }
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] < len) {
                return i;
            }
        }
        return nums.length;

    }

 

448. 找到所有数组中消失的数字

给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。

 

示例 1:

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

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

提示:

n == nums.length
1 <= n <= 105
1 <= nums[i] <= n
进阶:你能在不使用额外空间且时间复杂度为 O(n) 的情况下解决这个问题吗? 你可以假定返回的数组不算在额外空间内。

题解:

   public List<Integer> findDisappearedNumbers(int[] nums) {

        List<Integer> result = new ArrayList<>();
        int interval = nums.length + 1;
        for (int i = 0; i < nums.length; i++) {
            int targetRank = nums[i] % interval - 1;
            nums[targetRank] += interval;
        }

        for (int i = 0; i < nums.length; i++) {
            if (nums[i] < interval) {
                result.add(i + 1);
            }
        }
        return result;
    }

287. 寻找重复数

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。

你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。

 

示例 1:

输入:nums = [1,3,4,2,2]
输出:2
示例 2:

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

提示:

1 <= n <= 105
nums.length == n + 1
1 <= nums[i] <= n
nums 中 只有一个整数 出现 两次或多次 ,其余整数均只出现 一次
 

进阶:

如何证明 nums 中至少存在一个重复的数字?
你可以设计一个线性级时间复杂度 O(n) 的解决方案吗?

题解:

  public int findDuplicate(int[] nums) {

        int interval = nums.length + 1;
        for (int i = 0; i < nums.length; i++) {
            int targetRank = nums[i] % interval - 1;
            nums[targetRank] += interval;
        }

        int resultRank = -1;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > 2 * interval) {
                resultRank = i + 1;
            }
            nums[i] %= interval;
        }
        return resultRank;
    }

 

442. 数组中重复的数据

给你一个长度为 n 的整数数组 nums ,其中 nums 的所有整数都在范围 [1, n] 内,且每个整数出现 一次 或 两次 。请你找出所有出现 两次 的整数,并以数组形式返回。

你必须设计并实现一个时间复杂度为 O(n) 且仅使用常量额外空间的算法解决此问题。

 

示例 1:

输入:nums = [4,3,2,7,8,2,3,1]
输出:[2,3]
示例 2:

输入:nums = [1,1,2]
输出:[1]
示例 3:

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

提示:

n == nums.length
1 <= n <= 105
1 <= nums[i] <= n
nums 中的每个元素出现 一次 或 两次

 

题解

    public List<Integer> findDuplicates(int[] nums) {

        int interval = nums.length + 1;
        int interval2 = interval * 2;
        for (int i = 0; i < nums.length; i++) {
            int targetRank = nums[i] % interval - 1;
            nums[targetRank] += interval;
        }

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

 

41. 缺失的第一个正数

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
 

示例 1:

输入:nums = [1,2,0]
输出:3
示例 2:

输入:nums = [3,4,-1,1]
输出:2
示例 3:

输入:nums = [7,8,9,11,12]
输出:1
 

提示:

1 <= nums.length <= 5 * 105
-231 <= nums[i] <= 231 - 1

 

题解

    public int firstMissingPositive(int[] nums) {

        for (int i = 0; i < nums.length; i++) {
            if (nums[i] < 0 || nums[i] > nums.length) {
                nums[i] = 0;
            }
        }
        int interval = nums.length + 1;
        for (int i = 0; i < nums.length; i++) {
            int target = nums[i] % interval - 1;
            if (target >= 0) {
                nums[target] += interval;
            }
        }
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] < interval) {
                return i + 1;
            }
        }
        return nums.length+1;
    }

这个题不是直接可以使用 循环排序的算法.需要做一些转换为这种题.

 

总结:

这种题就是使用原本数组的 rank 和value之间的关系.

其中找出 rank和value下标之间转换的时候 可以适用 nums[rank] = nums[rank] + interval .interval选择比 当前序列中元素大一个的值.

这里需要思考返回值的时候可能会出现后一个的值,这种 需要看 rank和value之间的关系. 例如 41返回的是nums.length +1 .而268返回的是nums.lenght.这个当中主要是 rank +1 = value or rank = value的区别.

 

posted on 2022-09-19 00:55  爱喝冬瓜汤的萝卜  阅读(43)  评论(0编辑  收藏  举报