前端算法必刷题系列[92]

321 阅读2分钟

这是我参与8月更文挑战的第26天,活动详情查看:8月更文挑战

这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。

167. 寻找重复数 (find-the-duplicate-number)

标签

  • 中等

题目

leetcode 传送门

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数

假设 nums 只有一个重复的整数 ,找出 这个重复的数 。

你设计的解决方案必须不修改数组 nums 且只用常量级 O(1) 的额外空间。

示例 1

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

示例 2

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

示例 3

输入: nums = [1,1]
输出: 1

基本思路

看上去简单,但是其实还是要想想,怎么用 O(1) 空间解决。

暴力

任何题都有暴力解,我们最先思考暴力解其实是为了打开思路

找重复,很简单啊,遍历,找后面有没有重复的, js 中 api indexOflastIndexOf 看看一不一样就行了。代码就不写了。

二分法

这个不是排序数组,为什么可以二分 ?

我们读题,有 n+1 个数 其数字都在 1 到 n 之间, 说明这些数的取值范围有区间限定, [1..n]

  • 我们设定中间数 mid = (1 + n) / 2 把这个值域区间分成左右两份
    • [1, mid]
    • [mid + 1, n]
    • 重复的数字要么落在这两个区间之一,我们要怎么把它逼出来
  • 遍历数组,把小于等于 mid 的数量统计为 count
    • 如果 count <= mid, 说明对上了,前面左边区间没有重复的,重复数在 [mid + 1, n]
    • 反之,就在前面区间 [1, mid] 有重复
  • 接着就是继续二分上轮选中的区间了,思想其实跟二分没区别, 直到找到该数

写法实现

二分法

var findDuplicate = function(nums) {
  let l = 1,
    r = nums.length - 1

  const numslessOrEqMid = (mid) => {
    return nums.filter(it => it <= mid).length
  };

  while (l < r) {
    let mid = Math.floor((l + r) / 2)
    // 小于等于 mid 的数字个数
    let count = numslessOrEqMid(mid)
    if (count <= mid) {
      // 重复在右边区间 [mid + 1, n]
      l = mid + 1
    } else {
      // 重复在左边区间 [1, mid]
      r = mid
    }
  }

  return l
};


let nums = [1, 3, 4, 2, 2]
console.log(findDuplicate(nums))

快慢指针

其实我们就可以把这个数组想象成链表,一个连着一个,关键就在于如果有重复,就会连回来形成环, 我们找到入环口,就是找到了重复元素。

之前我们也写过 求入环点方法可以辅助看下

环形链表搞懂了,代码就非常清晰了。

写法实现

var findDuplicate = function(nums) {
  let slow = 0,
    fast = 0;
  // 因为必然有重复,直接循环让找到他们交界点
  // do..while 是为了让两个从 0开始的先走一步错开
  do {
    slow = nums[slow];
    fast = nums[nums[fast]];
  } while (slow != fast);
  // 然后重新找一个指针, 同步跟slow走就行
  let preOrder = 0;
  while (preOrder != slow) {
    preOrder = nums[preOrder];
    slow = nums[slow];
  }
  return preOrder;
};
let nums = [1, 3, 4, 2, 2]
console.log(findDuplicate(nums))

另外向大家着重推荐下这个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友 Or 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the rever monster,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧

参考