剑指Offer解题报告(Java版)——约瑟夫环 45
引言
常见的约瑟夫环问题有用循环链表做的,有用数组做的,这里提供一个用数学公式做的,由此可见,很多计算机的问题如果最终用到数学的知识,时间复杂度会大大的降低
分析问题
首先我们对0到n-1删除第一个数进行分析,第一个被删除的数一定是序号为m-1的数,因为0号数了1,1号数了2,m-1号数了m,那么应该删掉m-1号,设m-1号是第k号,这里这样做是因为后面可以扩展,想扩展为m-1%n=k
剩下的数按照序列重排序如下
因为删掉的是k,那么下面第一个就是k+1了,这个很好理解
假设我们设f函数的输出是原来的在n个数中,每次数m下,最后留下的数
而现在删掉了k,只有n-1个数了,每次数m下,最后留下的数是f'(n-1,m)
如何建立f和f'之间的关系呢
我们对重排序之后的数做一个映射
该映射为
p(x) -- > y ::: y=(x-k-1)%n
则映射完之后就跟原来的问题一样了,也就是f(n-1,m)了
也就是p(f'(n-1,m)=f(n-1,m)
而我们现在只有f(n,m)和f'(n-1,m)之间的关系
所以我们还需要再转换一下,也就求映射的逆
而映射p的逆映射为
o(y) -- >x x=(y+k+1)%n
其中k可以替换 k%n=(m-1)%n
即o(y) -- >x x=(y+m)%n
对应到之前f(n-1,m)和f'(n-1,m)之间的关系
f'(n-1,m)=f(n-1,m)+m%n
而
f(n,m)=f'(n-1,m)
这样我们有f(n,m)和f'(n-1,m)之间的关系,也有f(n-1,m)和f'(n-1,m)之间的关系
带入之前的式子推到得到
解决问题
static int lastRemain(int n, int m) {
int last = 0;
if (n < 1 || m < 1) {
return -1;
}
for (int i = 2; i <= n; i++)
last = (last + m) % i;
return last;
}