题意:0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
解题思路
解法1:--超时
1.遇到约瑟夫环,第一个想到的是,用循环链表的思维去解决问题
- 每次遍历,删除第m个节点,直到只剩余1个节点,但是这样的解法,有一个问题,数据量很大时,就会有超时问题
解法2:
1.既然循环链表思维超时,那么是否可以转换为DP问题,找寻规律
2.约瑟夫环是一个经典的数学问题,我们不难发现这样的依次报数,似乎有规律可循。为了方便导出递推式,我们重新定义一下题目。
问题: N个人编号为1,2,……,N,依次报数,每报到M时,杀掉那个人,求最后胜利者的编号。
- 每杀掉一个人,下一个人成为头,相当于把数组向前移动M位。若已知N-1个人时,胜利者的下标位置位f ( N − 1 , M ) ,则N个人的时候,就是往后移动M为,(因为有可能数组越界,超过的部分会被接到头上,所以还要模N),既
f ( N , M ) = ( f ( N − 1 , M ) + M ) % n
解题遇到的问题
无
后续需要总结学习的知识点
无
##解法1--链表操作方法,超时
class Solution {
public int lastRemaining(int n, int m) {
// 分析题意,n其实可以组成一个循环链表
// 每次遍历,删除第m个节点,直到只剩余1个节点
Node root = null, head = null;
int i = 0;
while (i < n) {
if (root == null) {
root = new Node(i);
head = root;
} else {
root.next = new Node(i);
root = root.next;
}
i++;
}
// 形成循环链表
root.next = head;
int j = 0;
// 下个元素是否就是自己,如果是自己,则只剩余一个元素
while (head.next != head) {
if (j == m - 1) {
// 跳过第m个元素
root.next = root.next.next;
j = 0;
head = root.next;
} else {
j++;
root = head;
head = head.next;
}
}
return head.val;
}
class Node {
int val;
Node next;
Node(int val) {
this.val = val;
}
}
}
##解法2
class Solution {
public int lastRemaining(int n, int m) {
int p = 0;
int i = 1;
while (i <= n) {
p = (p + m) % i;
i++;
}
return p;
}
}
网友评论