Skip to content

interview code #36

@scarcoco

Description

@scarcoco

问题描述

给定一个包含 n + 1 个整数的数组 nums,其数字都在 1n 之间(包括 1n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

示例 1:

输入: [1,3,4,2,2]
输出: 2

示例 2:

输入: [3,1,3,4,2]
输出: 3

说明:
  • 不能更改原数组(假设数组是只读的)。
  • 时间复杂度小于 O(n*n)
  • 数组中只有一个重复的数字,但它可能不止重复出现一次。

解法

不考虑空间复杂度:O(n)Array 或者 Map 缓存访问过的值

暴力方法(时间复杂度不满足)
function getRepeat(arr) {
    const len = arr.length
    let result
    for (let i = 0; i < len-1; i ++) {
        for (let j = i+1; j < len; j ++) {
            if (arr[i] === arr[j]) {
                result = arr[i]
            }
        }
        if (result !== undefined) {
            break
        }
    }
    return result
}
Map 缓存(可用数组)
function getRepeat(arr) {
    const ret = {}, len = arr.length
    let i
    for (i = 0; i < len; i++) {
        if (ret[arr[i]]) {
            break
        }
        ret[arr[i]] = 1
    }
    return i === len ? undefined : arr[i]
}
其他想法

如果确认只有一个元素重复,可以结合鸽笼原理和 N 个数求和相减即可。

面试官想要的解法

二分法猜元素,遍历查找,比如刚开始猜测试 M = Math.floor(N / 2),那么遍历小于 M 的元素个数,和大于 M 的元素个数,根据结果推断是在那边,然后继续,直到找到元素。

function getRepeat(arr) {
    const len = arr.length
    // n+1 => [1, n]
    let min = 1, max = len - 1
    let guess
    do {
        guess = Math.floor((min + max) / 2)
        let smallCount = 0, bigCount = 0

        for (let i = 0; i < len; i++) {
            if (arr[i] > guess) {
                bigCount ++ 
            } else if (arr[i] < guess) {
                smallCount ++
            }
        }
        if (smallCount > (guess - min)) {
            max = guess - 1
        } else if (bigCount > (max - guess)) {
            min = guess + 1
        } else {
            break
        }
    } while (min <= max)
    
    return guess
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions