每日两题 T10

时间:2022-07-22
本文章向大家介绍每日两题 T10,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

算法

LeetCode T面试题62. 圆圈中最后剩下的数字[1]

描述

0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

示例1

输入: n = 5, m = 3
输出: 3

示例2

输入: n = 10, m = 17
输出: 2

限制

1 <= n <= 10^51 <= m <= 10^6

分析

著名约瑟夫环问题,可以使用递归实现,但是有存在堆栈溢出问题。分析规律可以使用循环解决。

我们以 n=5, m=3为例

很明显我们每次删除的是第 mm 个数字,我都标红了。

第一轮是 [0, 1, 2, 3, 4] ,所以是 [0, 1, 2, 3, 4] 这个数组的多个复制。这一轮 2 删除了。

第二轮开始时,从 3 开始,所以是 [3, 4, 0, 1] 这个数组的多个复制。这一轮 0 删除了。

第三轮开始时,从 1 开始,所以是 [1, 3, 4] 这个数组的多个复制。这一轮 4 删除了。

第四轮开始时,还是从 1 开始,所以是 [1, 3] 这个数组的多个复制。这一轮 1 删除了。

最后剩下的数字是 3

图中的绿色的线指的是新的一轮的开头是怎么指定的,每次都是固定地向前移位 m 个位置。

然后我们从最后剩下的 3 倒着看,我们可以反向推出这个数字在之前每个轮次的位置。

最后剩下的 3 的下标是 0

第四轮反推,补上 m 个位置,然后模上当时的数组大小 2,位置是 (0 + 3) % 2 = 1

第三轮反推,补上 m 个位置,然后模上当时的数组大小 3,位置是 (1 + 3) % 3 = 1

第二轮反推,补上 m 个位置,然后模上当时的数组大小 4,位置是 (1 + 3) % 4 = 0

第三轮反推,补上 m 个位置,然后模上当时的数组大小 5,位置是 (0 + 3) % 5 = 3

所以最终剩下的数字的下标就是 3。因为数组是从 0 开始的,所以最终的答案就是 3

总结一下反推的过程,就是 (当前index + m) % 上一轮剩余数字的个数

复杂度分析

•时间复杂度:O(n)O(n),需要求解的函数值有n个。•空间复杂度:O(1)O(1),只使用常数个变量。

代码

/**
 * @param {number} n
 * @param {number} m
 * @return {number}
 */
var lastRemaining = function (n, m) {
  let res = 0;
  for (let i = 2; i != n + 1; ++i)
    res = (m + res) % i;
  return res;
}

前端

要求设计 LazyMan 类,实现以下功能

LazyMan('Tony');
// Hi I am Tony

LazyMan('Tony').sleep(10).eat('lunch');
// Hi I am Tony
// 等待了10秒...
// I am eating lunch

LazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
// Hi I am Tony
// I am eating lunch
// 等待了10秒...
// I am eating diner

LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food

分析

这是一个很典型的职责链调用问题,我们使用过 jQuery 应该不会陌生链式调用,但是我们发现现在功能中添加了异步操作,我们可以将需要调用的内容存入队列,然后逐步调用。

代码

class LazyManClass {
  constructor(name) {
    this.name = name
    this.queue = []
    console.log(`Hi I am ${name}`)
    setTimeout(() => {
      this.next()
    },0)
  }

  sleepFirst(time) {
    const fn = () => {
      setTimeout(() => {
        console.log(`等待了${time}秒...`)
        this.next()
      }, time)
    }
    this.queue.unshift(fn)
    return this
  }

  sleep(time) {
    const fn = () => {
      setTimeout(() => {
        console.log(`等待了${time}秒...`)
        this.next()
      },time)
    }
    this.queue.push(fn)
    return this
  }

  eat(food) {
    const fn = () => {
      console.log(`I am eating ${food}`)
      this.next()
    }
    this.queue.push(fn)
    return this
  }

  next() {
    const fn = this.queue.shift()
    fn && fn()
  }
}

function LazyMan(name) {
  return new LazyManClass(name)
}

References

[1] 面试题62. 圆圈中最后剩下的数字: https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/