leetcode 287. Find the Duplicate Number

本文介绍了一种在给定数组中查找重复数字的算法,利用桶排序和环检测两种方法,详细阐述了代码实现过程,并提供了具体示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、问题描述

二、代码实现

1、桶排序

2、环检测


 

https://leetcode.com/problems/find-the-duplicate-number/

给定一个元素值在[1, n]范围内、长度为n+1的数组(特性:至少有一个数字重复出现),假设只有一个重复数字,找到该数字。

 

一、问题描述

测试用例:

Example 1:
Input: [1,3,4,2,2]
Output: 2

Example 2:
Input: [3,1,3,4,2]
Output: 3

 

二、代码实现

1、桶排序

桶排序是将符合某种共同特性的元素,比如相同元素的数量放在同一个桶中(以元素值为下标的桶中)。这里采用类似桶排序的思想,遍历整个数组,将下标为当前元素值的元素做个已访问标记(将其乘以-1、将其乘以一个大于所有元素的因子factor、或者直接将其交换到下标为当前元素值的位置处),如果该元素已经访问过,则说明当前元素重复。

class Solution {
    
    //对于当前元素nums[i],将元素nums[nums[i]/factor]或nums[nums[i]]加上已访问标志(让其成为factor的倍数,从而大于等于factor)
    public int findDuplicate7(int[] nums) {
        int factor = nums.length;   //n+1
        for (int i=0; i<nums.length; i++) {
            int index = (nums[i] >= factor) ? nums[i] / factor : nums[i];
            if (nums[index] % factor == 0) {    //即nums[index]是factor的倍数,说明已经访问过
                return (nums[i] >= factor) ? nums[i] / factor : nums[i];
            }
            nums[index] = nums[index] * factor;
        }
        
        return -1;  //error-case
    }
    
    //对于当前元素nums[i],将元素nums[nums[i]%factor]加上已访问标志(将其加上factor的倍数,倍数大于等于1)
    public int findDuplicate6(int[] nums) {
        int factor = nums.length;   //n+1
        for (int i=0; i<nums.length; i++) {
            //int index = (nums[i] - 1) % factor;   //[3,1,3,4,2]
            int index = nums[i] % factor;
            if (nums[index] / factor != 0) {    //如果倍数不等于0,即倍数大于等于1,说明已经被访问过了
                return nums[i] % factor;
            }
            //nums[index] *= factor;    //[3,1,3,4,2]
            nums[index] = nums[index] + nums[index] * factor;    //添加已经被访问的标志
        }
        
        return -1;  //error-case
    }
    
    //对于当前元素nums[i],将元素nums[nums[i]]加上已访问标志(hashNum[nums[i]] == 1)
    public int findDuplicate5(int[] nums) {
         int[] hashNum = new int[nums.length];
        for(int i = 0; i < nums.length; ++i) {
            if(hashNum[nums[i]] == 0) {
                hashNum[nums[i]]++;
            } else {
                return nums[i];
            }
        }

        return -1;  //error-case
    }

    /对于当前元素nums[i],将元素nums[Math.abs(nums[i])]加上已访问标志(将其乘以-1)
    public int findDuplicate4(int[] nums) {
        int flag = -1;
        for (int i=0; i<nums.length; i++) {
            int index = (nums[i] > 0) ? nums[i] : -nums[i];
            if (nums[index] < 0) {
                return (nums[i] > 0) ? nums[i] : -nums[i];
            }
            nums[index] = nums[index] * flag;
        }
        
        return -1;  //error-case
    }
    
    //对于当前元素nums[i],将元素nums[Math.abs(nums[i])-1]加上已访问标志(将其乘以-1)
    public int findDuplicate3(int[] nums) {
        int length = nums.length;
        
        for (int i=0; i<length; i++) {
            int index = Math.abs(nums[i])-1;
            if (nums[index]<0) {    //已经被访问过了,说明Math.abs(nums[i])是重复元素
                return Math.abs(nums[i]);   //return index + 1;
            } 
            nums[index] = -nums[index];
        }
        
        return 0;   //error-case 
    }
    
    //对于当前元素nums[i],将元素nums[nums[i]]进行交换,使得nums[index] = index
    public int findDuplicate2(int[] nums) {
        int duplicate = 0;
        
        for (int i=0; i<nums.length; i++) {
            while (nums[i] != i) {
                if (nums[nums[i]] == nums[i]) {
                    duplicate = nums[i];
                    return duplicate;
                }
                int temp = nums[nums[i]];
                nums[nums[i]] = nums[i];
                nums[i] = temp;
            }
        }
        
        return duplicate;   //error-case
    }
    
    //对于当前元素nums[i],将其与元素nums[nums[i]-1]进行交换,使得nums[index] = index+1
    public int findDuplicate1(int[] nums) {
        for (int i=0; i<nums.length; ) {
            if (nums[i] == i+1) {
                i++;
                continue;
            }
            if (nums[nums[i]-1] != nums[i]) {    //nums[nums[i]]也可以
                swap(nums, i, nums[i]-1);
            } else {
                return nums[i];
            }
        }
        
        return -1;  //error-case
    }
    private void swap(int[] ?nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
    
    public int findDuplicate_error(int[] nums) {
        //[2,2,2,2,2]  输出6  期待2
        int ret = 0;
        for (int i=0; i<nums.length; i++) {
            ret ^= nums[i];
            if (ret == 0) {
                return nums[i];
            }
            
            if (i != nums.length-1) {
                ret ^= i+1;
            }
        }
        return ret;
    }
}

2、环检测

将元素值视为下一个待访问的元素的位置,由于数组肯定存在且只存在一个重复元素,访问序列肯定会不断重复一小段子序列。可以将其视为只带一个环的单链表(当前元素值是当前节点指向下一个节点的next指针),那么,问题就转变为找到环的入口点。

以测试用例[1, 3, 4, 2, 2]为例,访问序列为:1->3->2->4->2->4->...->2->4,环的入口点(元素2)就是其重复元素。

135 096 13213

class Solution {

    public int findDuplicate4(int[] nums) {        
        int fast = 0, slow = 0; //快慢指针
        
        //第一步:检测环(由于总有一个元素重复,环总存在)
        slow = nums[slow];
        fast = nums[nums[fast]];
        while(nums[slow] != nums[fast]) {   //快慢指针在环中总会相遇,退出循环时nums[slow]和nums[fast]就是相遇点的值
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
        
        //第二步:算出环的入口点
        fast = 0;
        while(nums[fast] != nums[slow]) {   //快慢指针再次相遇的点就是环的入口点————nums[fast]
            fast = nums[fast];
            slow = nums[slow];
        }
        
        return nums[fast];
    }    

}

 

3、

参考:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值