Update 62. 圆圈中最后剩下的数.md

补充约瑟夫环公式推导与示例图,并将递归实现调整为循环递推实现。
This commit is contained in:
CyC2018
2026-10-30 20:18:07 +08:00
parent 0ba7cc3d76
commit 2586cdbc14
2 changed files with 16 additions and 8 deletions
+16 -8
View File
@@ -10,16 +10,24 @@
## 解题思路
约瑟夫环,圆圈长度为 n 的解可以看成长度为 n-1 的解再加上报数的长度 m。因为是圆圈,所以最后需要对 n 取余。
约瑟夫环问题。设 f(n) 为 n 个人(编号 0~n-1)报到 m 出列时最后剩下的编号。第一轮出列的是编号 (m - 1) % n,之后从编号 m % n 开始报数,剩下 n - 1 个人。把他们从 m % n 开始重新编号为 0、1、2……,就变成了 n - 1 个人的同一个问题,答案 f(n - 1) 是新编号;新编号 x 对应的原编号为 (x + m) % n,因此:
```text
f(1) = 0
f(n) = (f(n - 1) + m) % n
```
例如 n = 5、m = 3,每一行是这一轮还剩下的人,从开始报数的人排起,上方是这一轮的新编号。最后剩下的 3 在每一行的新编号就是 f(5)~f(1),下一行到上一行编号加 3 再取余:
<div align="center"> <img src="pics/offer62-josephus.png" width="600px"/> </div><br>
num 最大为 10<sup>5</sup>,递归的调用深度与 num 相同,因此从 f(1) 开始递推:
```java
public int iceBreakingGame(int num, int target) {
if (num <= 0) {
return -1;
}
if (num == 1) {
return 0;
}
return (iceBreakingGame(num - 1, target) + target) % num;
int x = 0; /* f(1) = 0 */
for (int i = 2; i <= num; i++)
x = (x + target) % i; /* f(i) = (f(i - 1) + target) % i */
return x;
}
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB