约瑟夫环问题
题目
在罗马人占领乔塔帕特后,39 个犹太人与 Josephus 及他的朋友躲到一个洞中,39 个犹太人决定宁愿死也不要被敌人抓到,于是决定了一种自杀方式,41 个人排成一个圆圈,由第 1 个人开始报数,报数到 3 的人就自杀,然后再由下一个人重新报 1,报数到 3 的人再自杀,这样依次下去,直到剩下最后一个人时,那个人可以自由选择自己的命运。这就是著名的约瑟夫问题。现在请用单向环形链表得出最终存活的人的编号
思路
首先读懂题目的意思,从第一个开始报数,报到指定间隔的数就把该序号的人移除环形链表,如此反复循环,直到只剩下一个,举个形象的例子如下:
如果,现在有 7 个人围成一条环形链表,第一个开始,数到 3 ,就把对应的该位置的人移出去,假设移除的位置,用 ? 来代替,则有:
- [1, 2, 3, 4, 5, 6, 7],剩下 7 个
- 当前指针指向序号 1,移除第 3 个,剩下 [1, 2, ?, 4, 5, 6, 7]
- [1, 2, ?, 4, 5, 6, 7],剩下 6 个
- 当前指针指向序号 4,移除第 3 个,剩下 [1, 2, ?, 4, 5, ?, 7]
- [1, 2, ?, 4, 5, ?, 7],剩下 5 个
- 当前指针指向序号 7,移除第 3 个,剩下 [1, ?, ?, 4, 5, ?, 7]
- [1, ?, ?, 4, 5, ?, 7],剩下 4 个
- 当前指针指向序号 4,移除第 3 个,剩下 [1, ?, ?, 4, 5, ?, ?]
- [1, ?, ?, 4, 5, ?, ?],剩下 3 个
- 当前指针指向序号 1,移除第 3 个,剩下 [1, ?, ?, 4, ?, ?, ?]
- [1, ?, ?, 4, ?, ?, ?],剩下 2 个
- 当前指针指向序号 1,移除第 3 个,剩下 [?, ?, ?, 4, ?, ?, ?]
- 最后,剩下序号 4 存活
然而,实际上,每次在链表删除指定节点后,就没有 "?" 来顶替原来的位置
因此,如何能知道下一个相对的应该删除的位置是哪个?
思路一:模拟
模拟环中下一次要删除的位置
每次要从当前位置前进 m 个位置,如果知道上一个移动的位置 cur,那么用 cur + m 再对环的大小 size 取模,就是真正的要删除的位置
也就是,curPosition = ((curPosition - 1) + m) % size,这里 size 每次删除减一,所以前面位置 curPosition 相对于下一次也要减一才能对应上,例如 1 2 3 4 5 6 7,每次 3 格,第一次删除了 3,则下一次的 相对位置应该从 2 这个数字开始数,也就是 3 - 1
public class Main { public static void main(String[] args) throws IOException { josephRing(); } private static void josephRing() throws IOException { BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); String[] nums = bf.readLine().split(" "); int n = Integer.parseInt(nums[0]); //count of number int m = Integer.parseInt(nums[1]); //count of space LinkedList<Integer> list = new LinkedList<>(); for (int i = 1; i <= n; i++) { list.add(i); } int currentPos = 0; //position while (list.size() > 1) { currentPos = ((currentPos - 1) + m) % list.size(); //because size - 1 list.remove(currentPos); } System.out.println(list.peek()); } }
总结
牛客有两道约瑟夫环,第一道超时,第二道可以通过,好久没写,今天公司比赛,直接凉凉,需要平时多理解,多复习这个思想