如何洗牌除元素外的列表?
How to shuffle list except an element?
我有一个包含值元素的列表整数:0, 7, 2, 1, 6, 5.
我知道我可以使用方法
Collections.shuffle(list);
洗牌我的名单。但我不想改变第二个位置的值。它应该总是 7。
我该怎么做?
您可以将 Collection 打乱,然后将 7 恢复到第二位:
Collections.shuffle(list);
list.set(list.indexOf(7),list.get(2));
list.set(2,7);
或更短:
Collections.shuffle(list);
Collections.swap(list, 2, list.indexOf(7));
正如其他人所建议的那样,您还可以在改组之前删除您希望保留其位置的元素,然后再将其添加到同一位置。
ArrayLists 的两种方法应该花费相似的时间(在最坏的情况下是线性的),因为在我的回答中 indexOf
将花费线性时间,但是在替代解决方案中删除和添加一个元素(尤其是如果索引接近列表的开头)将花费 ArrayList
的线性时间,因为必须将 removed/added 索引之后的所有元素推送到新索引。
简单地防止移动任意数量的元素
- 将他们从列表中删除,
- 随机播放其余元素,
- 将它们放回原来的位置(从左开始以避免元素向右移动的问题)。
有一个更长的替代解决方案,但对于长列表可能更快:
public static <T> void shuffleExcept(final List<T> list, final int position) {
List<T> view = new AbstractList<T>() {
@Override
public T get(int index) {
return list.get(index >= position ? index+1 : index);
}
@Override
public T set(int index, T element) {
return list.set(index >= position ? index+1 : index, element);
}
@Override
public int size() {
return list.size()-1;
}
};
Collections.shuffle(view);
}
这里我们创建了一个 "view" 原始列表,它是除了我们要保留的元素之外的整个列表(我们只是移动了后续元素的索引)。接下来我们洗牌这个视图。这就是接口的美妙之处:您可以要求现有方法做一些不同的事情,只需传递新的接口实现即可。
用法示例:
List<Integer> input = Arrays.asList(0, 7, 2, 1, 6, 5);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
典型输出:
[6, 7, 5, 2, 1, 0]
[5, 7, 6, 1, 2, 0]
[6, 7, 2, 0, 5, 1]
[6, 7, 2, 0, 5, 1]
[2, 7, 0, 5, 6, 1]
[6, 7, 0, 2, 5, 1]
[5, 7, 2, 0, 1, 6]
我有一个包含值元素的列表整数:0, 7, 2, 1, 6, 5.
我知道我可以使用方法
Collections.shuffle(list);
洗牌我的名单。但我不想改变第二个位置的值。它应该总是 7。
我该怎么做?
您可以将 Collection 打乱,然后将 7 恢复到第二位:
Collections.shuffle(list);
list.set(list.indexOf(7),list.get(2));
list.set(2,7);
或更短:
Collections.shuffle(list);
Collections.swap(list, 2, list.indexOf(7));
正如其他人所建议的那样,您还可以在改组之前删除您希望保留其位置的元素,然后再将其添加到同一位置。
ArrayLists 的两种方法应该花费相似的时间(在最坏的情况下是线性的),因为在我的回答中 indexOf
将花费线性时间,但是在替代解决方案中删除和添加一个元素(尤其是如果索引接近列表的开头)将花费 ArrayList
的线性时间,因为必须将 removed/added 索引之后的所有元素推送到新索引。
简单地防止移动任意数量的元素
- 将他们从列表中删除,
- 随机播放其余元素,
- 将它们放回原来的位置(从左开始以避免元素向右移动的问题)。
有一个更长的替代解决方案,但对于长列表可能更快:
public static <T> void shuffleExcept(final List<T> list, final int position) {
List<T> view = new AbstractList<T>() {
@Override
public T get(int index) {
return list.get(index >= position ? index+1 : index);
}
@Override
public T set(int index, T element) {
return list.set(index >= position ? index+1 : index, element);
}
@Override
public int size() {
return list.size()-1;
}
};
Collections.shuffle(view);
}
这里我们创建了一个 "view" 原始列表,它是除了我们要保留的元素之外的整个列表(我们只是移动了后续元素的索引)。接下来我们洗牌这个视图。这就是接口的美妙之处:您可以要求现有方法做一些不同的事情,只需传递新的接口实现即可。
用法示例:
List<Integer> input = Arrays.asList(0, 7, 2, 1, 6, 5);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
shuffleExcept(input, 1);
System.out.println(input);
典型输出:
[6, 7, 5, 2, 1, 0]
[5, 7, 6, 1, 2, 0]
[6, 7, 2, 0, 5, 1]
[6, 7, 2, 0, 5, 1]
[2, 7, 0, 5, 6, 1]
[6, 7, 0, 2, 5, 1]
[5, 7, 2, 0, 1, 6]