剑指Offer——数组中重复的数字

1,257 阅读3分钟

题目:找出数组中重复的数字

在一个长度为n的数组里所有的数字都在0~n-1的范围内。数组中某些数组是重复的,但不知道有几个数组重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。要求时间复杂度为O(n),空间复杂度为O(1); 例如:如果输入长度为7的数组[2,3,1,3,3,6],那么对应的输出的重复数字为3;

解题思路

思路一:

通过将数组排序变成一段有序数组,取数组的第0个元素与第1个元素比较,如果相等找到重复数字;不相等拿第1个元素与第2个比较,相等则找到重复数字,否则依次比较下去。 但是排序一个长度为n的数组需要O(nlogn)的时间复杂度;

思路二:

利用hash表来解决;从头到尾按照顺序扫描数组的每个数组,每扫描一个数字的时候,判断哈希表里是不是已经包含这个数字。如果哈希表没有这个数字,则把它加入到哈希表中。存在的话表示已经找到一个重复素组。算法的时间复杂度O(n),但是创建了一个O(n)的哈希表,空间复杂度不相符;

思路三:

数组长度为n,数组中的数字都在0~n-1的范围内,如果这个数组中没有重复数字,那么当数组排序之后数字i将出现在下标为i的位置。由于数组中有重复数字,有些位置可能存在多个数字,同时有些位置可能没有数字。时间复杂度为O(n),空间复杂度为O(1);

解题代码

  private static Boolean duplication(int[] number, int length) {
        if (number == null && number.length <= 0) {
            return false;
        }
        for (int i = 0; i < length; i++) {
            while (number[i] != i) {
                if (number[i] == number[number[i]]) {
                    System.out.printf("重复数字 ".concat(String.valueOf(number[i])));
                    return true;
                }
                swap(number, i, number[i]);
            }
        }
        return false;
    }

    private static void swap(int[] number, int i, int j) {
        int n = number[i];
        number[i] = number[j];
        number[j] = n;
    }

接下来改变下题目,不修改数组找出重复的数字又该如何做?

不修改数组找出重复的数字

在一个长度为n的数组中所有数字都在0~n-1的范围内,所有数组中至少有一个数字是重复的。找出数组中任意一个重复的数字,但不能修改输入的数组。

解题思路

思路一:

创建一个长度为n的辅助数组,逐一把原数组的每一个数按照数值复制到辅助数组相对应的下标中,这样很容易知道那个数字有没有重复。但是空间复杂度为O(n)

思路二:

首先想想为什么会出现重复数字?假如没有重复数字,那么从1~n的范围内只有n个数字。由于数组里包含超过n个数字,所以一定包含重复的数字。例如:{2,2,4,1,3,5,6,5},长度为8的数组,所有的数字都在1-7中间,将1-7分为2段,一段为1-4,另一段为5-7,统计1-4中间数字出现的次数为5

解题代码

  private static int getDuplication(int[] number, int length) {

        if (number == null && number.length <= 0) {
            return -1;
        }
        int start = 1;
        int end = length - 1;
        while (end >= start) {
            int mid = ((end - start) >> 1) + start;
            int count = countRange(number, length, start, end);
            if (end == start) {
                if (count > 1) {
                    return start;
                } else {
                    break;
                }
            }

            if (count > (mid - start + 1)) {
                end = mid;
            } else {
                    start = mid + 1;
            }
        }

        return -1;
    }

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

    }

源码链接

后记

摘自《剑指Offter》