问题
这个问题的由来还是很有趣的,有一个叫约瑟夫的历史学家和40个士兵被罗马军队围困在一个洞穴中,他们拒绝投降而选择了自杀。这个自杀的方式还是很有创意的。41个人站成一个圈,从某个人开始,这个人将会杀死从他开始顺时针数到的第k个人,剩下的人接着数,最后就剩一个人,那个人自杀。死人的顺序就是约瑟夫排序。
现在给出一个初始化数组t,和每次数人的个数k,要求返回约瑟夫排序的结果。
例子
[1,2,3,4,5,6,7] - initial sequence
[1,2,4,5,6,7] => 3 is counted out and goes into the result [3]
[1,2,4,5,7] => 6 is counted out and goes into the result [3,6]
[1,4,5,7] => 2 is counted out and goes into the result [3,6,2]
[1,4,5] => 7 is counted out and goes into the result [3,6,2,7]
[1,4] => 5 is counted out and goes into the result [3,6,2,7,5]
[4] => 1 is counted out and goes into the result [3,6,2,7,5,1]
[] => 4 is counted out and goes into the result [3,6,2,7,5,1,4]
我的代码
package codewars;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Josephus {
static class Node {
Object value = 0;
Node next = null;
}
public static <T> List<T> josephusPermutation(final List<T> items, final int k) {
List<T> result = new ArrayList<T>();
// 如果为空或只有一个元素,那么直接返回
if (items.size() <= 1)
return items;
// 保存第一个节点,用于让最后一个节点指向它,从而构成循环列表
Node head = new Node();
// 指向当前元素的指针
Node pointer = null;
// 指向前一个元素指针,主要用于删除链表的节点
Node pre = null;
// 构建单向循环链表
for (int i = 0; i < items.size(); i++) {
if (i == 0) {
head.value = items.get(i);
pointer = head;
} else {
Node node = new Node();
node.value = items.get(i);
pointer.next = node;
pointer = pointer.next;
}
}
pointer.next = head;
// 开始取数据
while (pointer.next != null) {
for (int j = 0; j < k; j++) {
pre = pointer;
pointer = pointer.next;
}
result.add((T)pointer.value);
pre.next = pointer.next;
pointer = pre;
if (pointer == pointer.next) {
pointer.next = null;
}
}
result.add((T)pointer.value);
return result;
}
public static void main(String[] args) {
List result = josephusPermutation(Arrays.asList(new Object[]{"C"}), 4);
System.out.println(result);
}
}
别人的代码
import java.util.ArrayList;
import java.util.List;
import java.util.Collection;
public class Josephus {
public static Collection<Object> josephusPermutation(final List<Object> items, final int k) {
Collection<Object> permutation = new ArrayList<Object>();
int position = 0;
while(items.size() > 0) {
position = (position + k - 1) % items.size();
permutation.add(items.remove((int) position));
}
return permutation;
}
}
分析
我选择先将数组构建成一个循环列表,再不断循环取数据。别人的代码就显得比较简单,直接用取模的方式。List.remove 这个方法不但能删除元素,还会返回被删除的元素。这种取模的方法我也考虑过,就是比较担心如果是传入的参数是ArrayList的话,删除元素的性能会比较差,所以才构建了链表。