💯【算法面试】Go题解 1.找出数组中重复的数字 | 刷题打卡

645 阅读4分钟

个人评价:基础面试题,通常面试校招、1~2年开发者

题目难度:🌟 (参考 LeeCode 简单🌟 、中等🌟 🌟 、困难🌟 🌟 🌟 )

另,通常算法题会考察分析时间复杂度和空间复杂度,可参考笔者之前的文章

「数据结构&算法」必知必会系列:3. 时间 & 空间复杂度(下)

找出数组中重复的数字

题目描述

剑指 Offer 03

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:[2, 3, 1, 0, 2, 5, 3]

输出:2 或 3

限制

2 <= n <= 100000

解题思路

方法一

遍历数组,记录每个数字出现的次数,若出现过一次,将其返回即可。

这里,笔者使用 Golang 解题,应该不难看懂(次数存入一个 map 映射)

func findRepeatNumber(nums []int) int {
    numMap := make(map[int]int)
    for _, n := range nums {
      if _, ok := numMap[n]; ok {
          return n
      }
      numMap[n]=1
    }
    return -1
}

面试官可能会问下时间复杂度 & 空间复杂度?

🙌 时间 O(n) ;空间 O(n)

还有其他方法么?时间或者空间复杂度是否仍可优化?继续 ⏬

方法二

先排序,再比较相邻数,相同的即返回

func findRepeatNumber(nums []int) int {
    sort.Ints(nums)
    for i, v := range nums {
        if i>0 && v==nums[i-1] {
            return v
        }
    }
    return -1
}

用此方法,(额外)空间复杂度降为了 O(1),但是改变了原数组

那,时间复杂度呢?

🙌 这里,就需要了解 Go Sort 包方法的具体实现了 (✧◡✧) 回答得好面试绝对加分~

golang.org/src/sort/so…

sort 包会根据实际数据自动选择高效的排序算法 🐂

sort 包实现了四种基本排序算法:插入排序、归并排序、堆排序和快速排序。 但是这四种排序方法是不公开的,它们只被用于 sort 包内部使用。

有时间的 TX 可以深入看下源码,排序的注释很清晰明了,还加了复杂度说明 👍

可用作排序算法的代码示例 (^U^)ノ~YO

上述题解,对[]int 切片排序更常使用 sort.Ints(),时间复杂度 O(nlogn) (快排)

总结一下:空间复杂度↓ O(1),时间复杂度↑ O(nlogn) ,那该方法还有什么弊端么?

🙌 其实前面提到了,改变了原有数组为排序后的 (想下如何能不改变?)

还有就是,返回了最小的重复数字而不是原顺序的第一个

方法一 & 方法二 示例代码 playplay.golang.org/p/4p_KFSTbE…

那么,还有其他的方法么?令人头秃 👴 ...

方法三

其实方法一和方法二都没有用到题目中一个条件:「nums里所有的数字都在0 ~ n-1的范围内」

利用这个条件,就可以有方法三的神奇解法(原地置换法),思路如下:

  1. 数组由下标表示,我们把对应的数值交换放置到对应的数组下标位,如 2 =》arr[2]

  2. 遍历数组,我们做了换位+判断下标位数字是否已存在,若存在=》就是它了

直接上代码:

func findRepeatNumber(nums []int) int {
    for i, v := range nums {
        if i != v {
            if nums[v] == v {
                return v
            }
            nums[v] = v
        }
    }
    return -1
}

下图用测试数组理解下(下标表示循环到的 i 位置):

此方法下,时间复杂度 O(n),空间复杂度 O(1) 但是,原数组不再有用。

play.golang.org/p/LtRxfx95H…

总结

通常方法一解题比较稳妥,但是,可反问面试官对空间复杂度是否有要求,顺手给出方法二(须了解内置排序哦)

为啥不推荐直接方法三呢?方法三前提是条件nums里所有的数字都在0 ~ n-1的范围内 ,通常面试题并不会加这个条件😂。当然,如果给了,你还想得起来此方法并要能自圆其说,面试官还是会承认你是刷题小机灵鬼的。

此题难度一星其实是有争议的,能自己想到方法三,那还真是有慧根呢~

题目引申

找重复还是比较切合实际应用的,也很可能会问到如何去重?(这通常考察代码实操)

先给下 GO 解法,其实就是构造 map 映射,已存在则不加入d

// 去重
func uniqueArr(m []int) []int {
   d := make([]int, 0)
   tempMap := make(map[int]bool, len(m))
   for _, v := range m { // 以值作为键名
      if tempMap[v] == false {
         tempMap[v] = true
         d = append(d, v)
      }
   }
   return d
}

其他语言,如内置set(集合),像 Python、JavaScript等,则可更便捷

如 python (注意保持原有排序可加分~):

l2 = list(set(l1))  #乱序
l2.sort(key=l1.index) #原有排序

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情