约瑟夫问题的定义:假设n个人排成环形,且有一个正整数 m <= n。从某个指定的开始,沿环报数,每遇到第m个人
就让其出列,且报数进行下去。这个过程一直进行到所有人都出列为止。每个人出列的次序定义了整数0,1,2,...n-1
的(n, m)-约瑟夫排列。例如(7, 3)约瑟夫排列为<2,5,1,6,4,0,3>。
原始办法是非常直观的O(mn)算法。
分析约瑟夫排列问题,
首先可以坑定约瑟夫环的运算时间绝对大于等于O(n),这点毫无疑问。
还有就是数据大小与约瑟夫序列毫无关系。
只有序列的下标位置与约瑟夫排列有关。
一个约瑟夫排列的产生取决于两个量,一个是总数n,另一个是m,也就是每次跳跃的数量。开始是第一个人m号出列,然后是2m号,但是约瑟夫排列还存在循环问题。
继续看O(n)这个时间,这真的是约瑟夫排列的下线吗?是否存在一个约瑟夫排列他的运行时间是O(n)?
回答是:存在,当m = 1时,获得约瑟夫排列的时间是O(n)。
在看看约瑟夫环是否有最坏时间呢?
加入m = n,会发生什么事情呢?这样的情况下,没有具体的数据结构和算法是不能预估其计算时间的。
为了获得约瑟夫环,我目前只知道m和n能决定约瑟夫排列。对于每一个数据来说,它出现在最终排列的位置在[1,n]之间,这个最终排列的位置取决于什么呢?
可能的有:
1.这个数据的索引位置i
2.总数据量n
3.跳过间歇m
这三个量是影响这个数据在最终序列的位置的可能的关键变量。
仔细看也不全是这样。
如果当m = 1的时候,总数据量n并不会影响第i个数据出现在最终约瑟夫序列中的位置。
还有当i%m = 0 的时候,同样n也不会影响第i个数据出现在最终序列的位置。
这个例子可以看出,i,m,n之间的关系绝对是分情况的,这种情况可能是常量种情况或者随着n和m的变化而变化的情况数量。
变量种情况是不利于实现的。
实现这样一个约瑟夫序列生成的问题目前有以下几个:
1.如何快速实现从第i个输出的节点到第i+1个输出节点的查找?
换句话说就是如何在已经输出了i个节点的情况下,怎么快速判定,第i+1个输出节点。也就是和第i个输出的节点间隔m的那个节点。
问题可以稍微化简一点。
如果知道当前的节点总数n,和上一个输出节点x(其在n中的下标为x-index),那么我要知道下一个输出的数据是x后面第m个数。从全局来看就是从一开始第
(x-index + m)%n个数,输出之后n自减。换句话说我现在只需要每次求第(x-index + m)%n个数即可。从每次循环的过程来看。
假设一开始有n0个元素。
那么第一个输出的数据的下标为 x-index0 = m
第二个输出的数据在n0-1个元素中的位置为 x-index1 = (x-index0 + m - 1)%(n0-1)
第三个输出的数据在n0-2个元素中的位置为 x-index2 = (x-index1 + m - 1)%(n0-2)
。。。。。。
为了查找地x-index个位置的元素数据,
最快可以O(1)的时间,结合数组的数据结构,但是不便于维护。因为数组的元素下标固定,并且在上面每一次递归过程中都可能会发生改变,所以单纯的数组是不便利的。
由于这样的原因则只能抛弃使用下标来索引数据。但是为了快速获得第x-index个数据,除了下标直接索引之外就是使用二分法以O(logn)来查找这个数据。使用二分法的基本条件是需要有顺序关系,这一点上有很多数据结构都满足条件。
现在已经获得两个数据结构的有关特性了。
1.不能直接使用下标,这意味着数据可能是动态分配的。(因为静态分配大多都是连续空间)
2.需要保持数据之间的先后关系,以便于二分查找。
而且每次递归的过程中数据规模都会减1,也就是会删除掉第一个数据,不在放入下一次递归中去。
所以条件3是:
3.能够快速删除的数据结构。
还有最重要的是能够快速索引到第i个数。
4.能够快速索引的数据结构。
有了这几个相关信息,明显在说红黑树,不过也是存在别的方案的。由于无法使用O(1)的索引方式获取数据,只能使用二分查找来获取。那么二分法时间就是O(logn),对于n个数据来说总时间至少是n*O(logn) = O(nlogn)的最低时间,因为还要考虑删除的时间,如果删除的