我正在尝试解决这样一个难题:100个人围成一圈。第一个人杀死他旁边的人并将枪交给下一个人。最后还剩下哪个人?
这是我到目前为止所做的,但是当我运行它时,它会显示一个超出范围的异常。我意识到当我编写people.remove(i + 1)时,程序会运行到arraylist的末尾,并且无法在开始时重新开始继续该模式。我该怎么做呢?
感谢您的帮助!
private void btnEnterActionPerformed(java.awt.event.ActionEvent evt) {
int input = Integer.parseInt(txtInput.getText());
ArrayList <Integer> people = new ArrayList <> ();
for (int i = 0; i < input; i++) {
people.add(i);
}
while (people.size() != 0) {
int i = 1;
people.remove(i+1);
i++;
}
for (int i = 0; i < people.size(); i++) {
lblOutput.setText(" " + people.get(i));
}
答案 0 :(得分:0)
您获得越界异常的原因是您检查的大小是非零,但remove(i+1)
的调用i
设置为1
意味着从列表中的第三个位置,可能不存在。只保证索引为零的初始元素。
另请注意,i++
无效,因为i
会重置回循环体顶部的1
。
条件people.size() != 0
唯一保证的是你可以在零指数处删除。但是,这是相当低效的,因为需要复制经过该索引的所有元素。这使得删除O(n 2 ),当列表真的很长时,这可能会很慢。
但是,通常情况下,从列表中管理删除的惯用方法是使用ListIterator<T>
删除零到几个项目,或者复制到单独的列表中并在需要时用新的列表替换原始列表删除列表的重要部分。
答案 1 :(得分:0)
据我所知,您需要从列表中删除每个第二个人,直到只剩下一个人。
您当前实施的基本问题是,首先,您不进行任何范围检查(您如何知道i+1
中实际存在的元素),其次,您循环直到列表为止空的,这不是你真正想要的。
基本要求可以使用复合循环,外部循环检查列表的大小并保持循环,而List
的大小大于1
,第二个循环处理列表,删除列表中的其他所有人。注意,我没有重置外循环中的hasGun
标志,这意味着在内循环的每次迭代中,枪继续传递给下一个幸存者。
ArrayList<Integer> people = new ArrayList<>();
for (int i = 0; i < 10; i++) {
people.add(i);
}
boolean hasGun = true;
while (people.size() > 1) {
Iterator<Integer> iterator = people.iterator();
while (iterator.hasNext()) {
System.out.print("> " + iterator.next());
if (!hasGun) {
// You get shot...
iterator.remove();
System.out.println(" got shot");
} else {
System.out.println(" shoots");
}
hasGun = !hasGun;
}
}
for (Integer person : people) {
System.out.println(person);
}
此示例还使用了List
&#39; s Iterator
,这部分是数组越界问题,但您也可以使用{{1 }}循环和for-next
标志。
答案 2 :(得分:0)
要使用索引在数组中循环,请使用余数运算符:
int actual = 0;
while (people.size() != 1) {
people.remove( (actual+1) % people.size() );
actual = (actual+1) % people.size();
}
答案 3 :(得分:0)
我只是认为ArrayList
不是此问题的最佳数据结构。我发现LinkedList
会更合适。实际上,我发现了一个非常简单的递归解决方案。看看这段代码:
public class Main {
public static int kill(LinkedList<Integer> people) {
assert people.size() > 0;
System.out.println("people: " + people);
if (people.size() < 3)
return people.getFirst();
else {
System.out.println("kill: " + people.remove(1));
people.addLast(people.removeFirst());
return kill(people);
}
}
public static void main(String[] args) {
LinkedList<Integer> people = new LinkedList<>();
for (int i = 0; i <=100; i++) {
people.add(i);
}
int survivor = kill(people);
System.out.println("Last survivor: " + survivor);
}
}
我只是删除(kill?)列表中的第二个成员,然后将第一个成员发送回列表的末尾。这个过程可以重复,直到有2个人离开,在这种情况下,你可以猜测最后一个幸存者将是列表中的第一个幸存者,因为他将杀死第二个人。
答案 4 :(得分:0)
如果我必须解决此问题,我会创建自己的Person
类,其中next
属性指向下一个人。
Person
上课:
public class Person {
private int id;
private Person next;
public Person(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
public Person getNext() {
return this.next;
}
public void setNext(Person next) {
this.next = next;
}
public void killNext() {
this.next = this.next.next;
}
}
一旦到位,设置一组循环链接人员是微不足道的。然后,该算法通过遵循next
属性简单地循环每个人,在每次迭代时杀死next
个人。当next
属性指向自己时循环退出,表明没有人离开。
算法:
public static void main(String[] args) {
// Setup 100 persons in a linked circle.
Person startingPerson = new Person(1);
Person currentPerson = startingPerson;
for (int i = 2; i <= 100; i++) {
currentPerson.setNext(new Person(i));
currentPerson = currentPerson.getNext();
}
currentPerson.setNext(startingPerson);
// Loop around until a single person is left.
currentPerson = startingPerson;
while (currentPerson != currentPerson.getNext()) {
currentPerson.killNext();
currentPerson = currentPerson.getNext();
}
System.out.println("Surviving person: " + currentPerson.getId());
}
输出:
幸存者:73