剑指 Offer 62. 圆圈中最后剩下的数字 (约瑟夫问题)

题目

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^5
1 <= m <= 10^6

解题思路

  1. 使用列表模拟叫号、删除数字的过程
    如n=5, m=3
    下表下划线元素,为每次删除的元素,一次删除一个。
原始数据 0 1 2 3 4
第1次删除 0 1 2 3 4
剩余元素位置左移 0 1 3 4
第2次删除 0 1 3 4
剩余元素位置左移 1 3 4
第3次删除 1 3 4
剩余元素位置左移 1 3
第4次删除 1 3
剩余元素位置左移 3

注意到第i次删除操作,删除元素位置都是 (i + m -1 ) % n, n是当前元素个数(表长);
如果使用java编程,可以使用ArrayList自动维护 “剩余元素位置左移” 的操作。

伪码

lastRemaining(N, m)
      // 新建list 
      let list[0..n-1] = 0,1,2,...,n-1
      i = 0
      while list.size > 1 
            i = (i + m - 1) % list.size
            list.remove( i )
return list[0]
  1. 考虑第k次删除,和第k-1次删除之间的数学关系
位置 0 1 2 ... m-2 m-1 m m+1 ... n-1
第k-1次操作后 a0 a1 a2 ... a(m-2) a(m-1) a(m) a(m+1) ... a(n-1)
第k次删除 a0 a1 a2 ... a(m-2) a(m-1) a(m) a(m+1) ... a(n-1)
左移剩余元素 a(m) a(m+1) ... a(n-1) a0 a1 a2 ... a(m-2)

第k删除的元素的位置为 (m-1) % n, n = N-k,其中,N是元素初始个数,n是当前剩余元素个数(第k-1次操作后)。
相当于在第k-1次删除后的基础上,a[m..n-1]元素都往左移m个位置,a[0..m-2]右移 n-1 - m + 1 = n - m个元素。
 
记f(i, k),表示初始位置为i的元素,经历k次操作后的位置(前提是前k-1次,该元素都未被删除),有

\[f(i,k)= \begin{cases} f(i, k-1) - m& \text{f(i, k-1) > m-1}\\ f(i, k-1) + n - m& \text{f(i, k-1) < m-1} \end{cases}\]

上式可以综合到一起,

\[f(i,k)= [f(i, k-1) - m] \% n], 其中, f(i, k-1) ≠ m-1,n = N - (k-1) \]

已知什么,要求什么?
已知的是第k次删除元素后,只剩1个元素位置为0,即为所求元素。
也就是说,已知f(k=N-1) = 0,要求f(0),而a[f(0)]即为所求元素值。把f(k-1)写成f(k)的表达式。
推导过程,

\[\begin{aligned} &∵f(k) = [f(k-1)-m] \% n, n = N - (k-1)\\ &∴f(k-1) - m = xn + f(k), x∈Z\\ &∴f(k-1) = xn + f(k) + m, 式子两边对n求余\\ &f(k-1) \% n = [f(k) + m] \% n, 而f(k-1)就是元素在长度为n(n=N-(k-1))时的位置,\\ &也就是说, f(k-1)∈[0, n) \\ &故f(k-1) = f(k-1) \% n = [f(k) + m] \% n \end{aligned} \]

问题转化为

\[\begin{aligned} f(k=N-1) &= 0 \\ f(k-1) &= [f(k) + m] \% 2 \\ f(k-2) &= [f(k-1) + m] \% 3 \\ ...&\\ f(1) &= [f(0) + m] \% (N-1) \\ f(0) &= [f(1) + m] \% (N) \end{aligned} \]

这样,就可以利用程序进行循环/递归求解了。
伪代码

lastRemaining(N, m)
      k = N - 1
      pos = 0
      while k > 0
            pos= (pos + m) % (N - k + 1)
            k = k-1
      return pos

参考

剑指 Offer 62. 圆圈中最后剩下的数字

posted @ 2021-01-04 01:23  明明1109  阅读(86)  评论(0编辑  收藏  举报