LeetCode287——寻找重复数

https://blog.csdn.net/whdAlive/article/details/80459730

二分法

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间,包括 1 和 n ,可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

示例 1:

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

   
   
   
   
  • 1
  • 2

示例 2:

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

   
   
   
   
  • 1
  • 2

说明:

  1. 不能更改原数组(假设数组是只读的)。
  2. 只能使用额外的 O(1) 的空间。
  3. 时间复杂度小于 O(n2) 。
  4. 数组中只有一个重复的数字,但它可能不止重复出现一次。

分析

由于限制了空间同时不能更改原数组,导致我们不能使用 第一个缺失的数字 中的解法,即元素按其值放入指定位置。

分析一下这道题,如果正常情况没有重复数字,那么从1~n只有n个数字,当有一个重复数字时,1~n有n+1个数字。这样我们就可以将n个数字从其中间数字m处分为两半,如果1~m中数目超过m,那么重复数字出现在1~m,反之则出现在m+1~n中。

这么一看,这不是典型的二分法么?

代码

public int findDuplicate(int[] nums) {
		int len = nums.length;
		return findDuplicateCore(nums, 1, len-1);
	}

	public int findDuplicateCore(int[] nums, int begin, int end) {
		while (begin <= end) {
			if(begin == end) {
				return begin;
			}
			int mid = begin + (end - begin) / 2;
			int count = count(nums, begin, mid);
			if (count > (mid - begin + 1)) {
				return findDuplicateCore(nums, begin, mid);
			} else {
				return findDuplicateCore(nums, mid + 1, end);
			}
		}
		return -1;
	}

	public int count(int[] nums, int begin, int end) {
		int ret = 0;
		for (int i = 0; i < nums.length; i++) {
			if (nums[i] >= begin && nums[i] <= end) {
				ret++;
			}
		}
		return ret;
	}


Java版快慢指针简单实现

  • 将数组转化为链表形式:数组 [1,3,4,2,2]
  • current / index 0 1 2 3 4
    next / num[index] 1 3 4 2 2

    index为当前值的索引,num[index]为下个一值的索引next index。上表中的数组表示成链表如下图,方框中为index, num[index]

    pic.jpg

    1. 利用【142_环形链表 II】的方法,找到环入口,即为重复数字
      1. 设:slow指针移动速度为1,fast指针移动速度为2;slow指针在环内移动(非环部分)长度为a,slow指针在环内移动长度为b
      2. 两指针相遇时候,slow指针移动距离为a+b,fast指针移动距离为2(a+b),可知两指针距离差a+b即为整数倍的环长
      3. 从head移动a的距离为入环点;由2可知从head开始移动a+(a+b)的距离也为入环点,即将A点继续移动距离a则可到达入环点
      4. slow指针移动回head,同时同速移动两个指针,相遇点即为入环点

    说明:因为数组中不含0,所以不会因为index = 0, num[0] = 0导致死循环;对于其他位置index = num[index],若该值重复则会自身成环,若无重复则不会被遍历到

    public class FindTheDuplicateNumber {
        public int findDuplicate(int[] nums) {
            if (nums.length <= 1) {
                return -1;
            }
            // 共同起点是0
            int fast = nums[nums[0]];
            int slow = nums[0];
            // 第一次相遇
            while (fast != slow) {
                slow = nums[slow];
                fast = nums[nums[fast]];
            }
            // 寻找入环点,注意起点是0
            slow = 0;
            while (fast != slow) {
                slow = nums[slow];
                fast = nums[fast];
            }
            return fast;
        }
    }
    

    扩展阅读:
    每日一练:找出数组中的重复数字
    https://blog.csdn.net/qq_41889292/article/details/80470860

    你可能感兴趣的:(LeetCode)