Leetcode——寻找重复数

Posted Yawn,

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetcode——寻找重复数相关的知识,希望对你有一定的参考价值。

1. 寻找重复数


因为该题要求只能用常量级的空间,所以不能直接使用暴力set查重

(1)快慢指针

  • fast 和 slow 是指针, nums[slow] 表示取指针对应的元素
  • 注意 nums 数组中的数字都是在 1 到 n 之间的(在数组中进行游走不会越界),
  • 因为有重复数字的出现, 所以这个游走必然是成环的, 环的入口就是重复的元素,
  • 即按照寻找链表环入口的思路来做(参考环形链表2)

第一次相遇时,fast总的路程是slow总的路程加上n圈环。总fast=总slow+n圈环 也就是总fast=总slow+nb,而总fast的路程还是总slow路程的两倍,所以总fast=2总slow,所以两式得出总slow=nb,总fast=2nb。所以第一次相遇的时候slow就是已经走了nb,在这里n=1

  • 第一次相遇时慢指针已经走了nb步

从head结点走到入环点需要走 : a + nb, 而slow已经走了nb,那么slow再走a步就是入环点了。

  • 走a+nb步一定是在环入口
class Solution {
    public int findDuplicate(int[] nums) {
        int fast = 0, slow = 0;
        while (true) {
            fast = nums[nums[fast]];        //快指针走两步 fast = fast.next.next ==> 本题 fast = nums[nums[fast]]
            slow = nums[slow];				//慢指针走一步 slow = slow.next ==> 本题 slow = nums[slow]

            //找到第一个相遇点 fast = 2 * slow, fast = slow + nb => slow = nb, slow + a  = 入环点
            if (slow == fast) {             
                fast = 0;
                //fast回头节点开始走,恰好走a个位置到达入环点与slow + a相遇
                while(nums[slow] != nums[fast]) {       
                    fast = nums[fast];
                    slow = nums[slow];
                }
                return nums[fast];
            }
        }    
    }
}

(2)字典数组(一个萝卜一个坑)

class Solution {
    public int findDuplicate(int[] nums) {
        int i = 0;
        while (true) {
            //假如nums[i] == i + 1,说明这个坑位占的元素是对的,指针往后移
            if (nums[i] == i + 1) {
                i++;
            }
            //假如要放的坑位上的元素已经有这个元素了,说明找到重复数了,直接返回这个数字
            else if (nums[i] == nums[nums[i] - 1]) 
                return nums[i];
                
            //其他情况,指针对应元素不在应该放的坑位中,交换到自己对应的坑位。
            else swap(nums, i, nums[i] - 1);
        }
        
    }
    void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

(3)二分(结合抽屉原理)

二分查找的思路是先猜一个数(有效范围 [left…right] 里位于中间的数 mid),然后统计原始数组中 小于等于 mid 的元素的个数 count:

  • 如果 count严格大于 mid。根据抽屉原理,重复元素就在区间 [left…mid] 里;
  • 否则,重复元素就在区间 [mid + 1…right] 里。

与绝大多数使用二分查找问题不同的是,这道题正着思考是容易的,即:思考哪边区间存在重复数是容易的,因为有抽屉原理做保证。

public class Solution {

    public int findDuplicate(int[] nums) {
        int len = nums.length;
        int left = 1;
        int right = len - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            
            int cnt = 0;
            for (int num : nums) {
                if (num <= mid) {
                    cnt += 1;
                }
            }

            // 根据抽屉原理,小于等于 4 的个数如果严格大于 4 个,此时重复元素一定出现在 [1..4] 区间里
            if (cnt > mid) {
                // 重复元素位于区间 [left..mid]
                right = mid;
            } else {
                // if 分析正确了以后,else 搜索的区间就是 if 的反面区间 [mid + 1..right]
                left = mid + 1;
            }
        }
        return left;
    }
}

以上是关于Leetcode——寻找重复数的主要内容,如果未能解决你的问题,请参考以下文章

leetcode 287 Find the Duplicate Number寻找重复数

Leetcode——寻找重复数

LeetCode-287 寻找重复数

LeetCode #287 寻找重复数

LeetCode287. 寻找重复数

LeetCode287. 寻找重复数