剑指Offer-不修改数组找出重复的数字

97 阅读1分钟

题目

给定一个长度为 n+1n+1 的数组nums,数组中所有的数均在 1∼n1∼n 的范围内,其中 n≥1n≥1。

请找出数组中任意一个重复的数,但不能修改输入的数组。

数据范围 1≤n≤10001≤n≤1000 样例 给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。

返回 2 或 3。 思考题:如果只能使用 O(1)O(1) 的额外空间,该怎么做呢?

解析

抽屉原理:n + 1个球放到n个抽屉里,必然会有一个抽屉中球的个数大于等于2。
根据抽屉原理,然后利用二分的思想,将数取值的区间[1, n]分成[1, n/2]和[n/2, n],分别统计每个区间中数的个数,若有哪个区间数的个数大于等于区间长度,取的数一定在这个区间内,此时就将范围缩减到一半,然后依次进行下去,直到区间长度==1时,就得到最后的结果。

代码

C++

class Solution {
public:
    int duplicateInArray(vector<int>& nums) {
        int l = 1, r = nums.size() - 1;
        while (l <= r) {
            int s = 0;
            int mid = l + (r - l) / 2;
            for (auto x: nums)
                s += x >= l && x <= mid;
            if (s > mid - l + 1) r = mid - 1;
            else l = mid + 1;
        }
        return l;
    }
};

Python

class Solution(object):
    def duplicateInArray(self, nums):
        """
        :type nums: List[int]
        :rtype int
        """
        l, r = 1, len(nums) - 1
        while l <= r:
            mid = (l + r) // 2
            s = 0
            for x in nums:
                if x >= l and x <= mid:
                    s += 1
            if s > mid - l + 1:
                r = mid - 1
            else:
                l = mid + 1
        return l
        

Go

func duplicateInArray(nums []int) int {
    l, r := 1, len(nums) - 1
    for l <= r {
        mid := l + (r - l) / 2
        s := 0
        for _, v := range nums {
            if v >= l && v <= mid {
                s += 1
            }
        }
        
        if s > mid - l + 1 {
            r = mid - 1
        } else {
            l = mid + 1
        }
    }
    return l
}