一、题目
演示示例:
二、测试代码
//方法一 数组模拟队列
class Solution {
public int lastRemaining(int n, int m) {
ArrayList<Integer> list=new ArrayList<>();
for(int i=0;i<n;i++)//将0到n-1全部放入list中
{
list.add(i);
}
int index = 0;
while (n > 1) {//模拟删除固定位置的数字
index = (index + m - 1) % n;
list.remove(index);
n--;
}
return list.get(0);//直到list只剩下最后一个元素取出即可
}
}
//方法二 数学解法
class Solution {
public int lastRemaining(int n, int m) {
int ans = 0;//记录最后一个剩余数字的位置
for (int i = 2; i <= n; i++) // 最后一轮剩下2个人,即从2开始反推
{
ans = (ans + m) % i;
}
return ans;
}
}
三、运行情况
方法一:
方法二:
四、刷题总结
本题是典型的约瑟夫环问题。
1、方法一的主要思路:我们利用ArrayList将0~n-1存放到其中,再对ArrayList模拟对固定位置数字的删除直到剩余最后一个数字。假设当前删除位置是index,则下一个删除数字位置为index+m。但因为将当前位置的数字删除了,后面的数字会向前移动一位,则实际的下一个删除数字的位置为index+m-1。又因为遍历一遍后会继续从头再次遍历,我们可以采用取模的方法,即(index+m-1) % n.
2、方法二的主要思路:我们以一个实例对n=5,m=3来进行解释.
第一轮为[ 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,下标为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。
即为本题的思路,我们采用反推方法,即(当前的index下标+m)%上一轮剩余数字个数,