《剑指offer》3.数组中重复的数字

本文探讨了在数组中查找重复数字的不同算法实现,包括排序、HashMap和原地置换等方法,并介绍了如何在不修改原始数组的情况下使用二分查找法解决升级版问题。

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

注:此博客不再更新,所有最新文章将发表在个人独立博客limengting.site。分享技术,记录生活,欢迎大家关注

原题链接:3.数组中重复的数字

题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。

思路:
找数组中其中一个重复的数字/全部重复数字:①HashMap ②如果n个数字都在0~n - 1范围内,则可以将数组排成nums[i] == i 的形式 ③ 如果要求不能修改数组且空间复杂度O(1),可以对值的一定范围内的元素个数进行二分查找

第一次提交:排序,时间复杂度O(nlogn),空间复杂度O(1)

import java.util.Arrays;

public class Solution {
    // Parameters:
    //    numbers:     an array of integers
    //    length:      the length of array numbers
    //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
    //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
    //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
    // Return value:       true if the input is valid, and there are some duplications in the array number
    //                     otherwise false
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        Arrays.sort(numbers);
        for (int i = 1; i < length; i ++) {
            if (numbers[i] == numbers[i - 1]) {
                duplication[0] = numbers[i];
                return true;
            }
        }
        return false;
    }
}

结果:

答案错误:您提交的程序没有通过所有的测试用例
case通过率为83.33%

测试用例:
[]

对应输出应该为:

"false,-1"

你的输出为:

java.lang.NullPointerException

经验:应该先写测试用例,首先考虑特殊情况

import java.util.Arrays;

public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        // 添加numbers为空时的情况
        if (length <= 0) { 
            duplication[0] = -1;
            return false;
        }
        Arrays.sort(numbers);
        for (int i = 1; i < length; i ++) {
            if (numbers[i] == numbers[i - 1]) {
                duplication[0] = numbers[i];
                return true;
            }
        }
        return false;
    }
}
运行时间:27 ms	

占用内存:8756K

第二次提交:用HashMap,时间复杂度O(1),空间复杂度O(n)

import java.util.HashMap;
import java.util.Map;

public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        if (length <= 0) {
            duplication[0] = -1;
            return false;
        }
        
        Map<Integer, Integer> map = new HashMap<>();

        for (int i = 0; i < length; i ++) {
            if (map.containsValue(numbers[i])) {
                duplication[0] = numbers[i];
                return true;
            }
            map.put(i, numbers[i]);
        }
        return false;
    }
}
运行时间:14ms

占用内存:9556k

第三次提交:利用数组长度为n且所有数字都在1~n-1重排数组,使得num[i] == i,时间复杂度O(1),空间复杂度O(1)

public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
        if (length <= 0) {
            duplication[0] = -1;
            return false;
        }

        for (int i = 0; i < length; i++) {
            int m = numbers[i]; // 0 <= numbers[i] <= n - 1
            if (m != i) {
                if (m != numbers[m]) {
                    numbers[i] = numbers[m];
                    numbers[m] = m;
                } else {
                    duplication[0] = numbers[m]; // numbers[m] == numbers[i]
                    return true;
                }
            }
        }
        return false;
    }
}
运行时间:15ms

占用内存:9540k

与我不同的排名前列答案:

public class Solution {
    public boolean duplicate(int numbers[],int length,int [] duplication) {
	    StringBuffer sb = new StringBuffer(); 
        for (int i = 0; i < length; i ++) {
	        sb.append(numbers[i] + "");
        }
        for (int j = 0; j < length; j ++) {
            if (sb.indexOf(numbers[j] + "") != sb.lastIndexOf(numbers[j] + "")) {
                duplication[0] = numbers[j];
                return true;
	        }
        }
        return false;
    }
}
运行时间:24ms

占用内存:8784k

升级版:不修改数组找出重复的数字
在一个长度为n+1的数组里的所有数字都在1到n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字。 例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。

如果用上面第三次提交的方法,由于不能修改数组则需要另外开辟O(n)空间的数组,时间复杂度O(1),空间复杂度O(n)。

如果用二分查找只需要O(1)空间复杂度,时间复杂度为O(nlogn),因为函数countRange()将被调用O(logn)次,每次需要O(n)时间:

关于二分查找时间复杂度为O(logn)的解释:
二分查找的基本思想是将n个元素分成大致相等的两部分,去a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果x<a[n/2],则只要在数组a的左半部分继续搜索x,如果x>a[n/2],则只要在数组a的右半部搜索x.

时间复杂度无非就是while循环的次数!

总共有n个元素,

渐渐跟下去剩余将要操作的元素数目就是n,n/2,n/4,....n/2^k,其中k就是循环的次数

由于你n/2^k取整后>=1

即令n/2^k=1

可得k=log2n,(是以2为底,n的对数)

所以时间复杂度可以表示O(logn)
public class Solution {
    public static int duplicate(int numbers[],int length) {
        if (length <= 0) {
            return  -1;
        }

        // 1 <= numbers[i] <= n
        int start = 1;
        int end = length - 1; // end = length - 1 = n + 1 - 1 = n
        while (end >= start) {
            int middle = start + (end - start) / 2;
            int count = countRange(numbers,length,start,middle);
            if (end == start) {
                if (count > 1) return start;
                else break;
            } else if (count > middle - start + 1) {
                end = middle;
            } else {
                start = middle + 1;
            }
        }
        return -1;
    }

    public static int countRange(int[] numbers, int length, int start, int end) {
        if (length <= 0) return 0;
        int count = 0;
        for (int i = 0; i < length; i ++)
            if (numbers[i] >= start && numbers[i] <= end)
                count++;
        return count;
    }

    public static void main(String[] argv) {
        int[] nums = {1,3,4,1};
        int dup = duplicate(nums, nums.length);
        System.out.println("duplicate is " + dup);
    }
}

但是在某些特殊输入时二分搜索无法找出重复的数,如输入{1,3,4,1}则输出为-1。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值