此处详细描述了问题:https://adventofcode.com/2015/day/3 总结一下:由于文本文件中的指示,我先后访问了地点,我想知道我至少访问过多少个地点。
我创建了一个类Point:
public class Point {
private int x;
private int y;
public Point (int x, int y) {
this.x=x;
this.y=y;
}
public int getAbs() {
return x;
}
public int getOrd() {
return y;
}
}
它只是一个由两个坐标组成的点,用于指示房屋的位置。 我在这里用它:
public class Exo3 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("C:/Users/mpv15_000/Documents/Advent/input3.txt");
int c = fr.read();
int x=0;
int y=0;
Point init = new Point(x,y);
List<Point> visitedHouses = new ArrayList<Point>();
visitedHouses.add(init);
Boolean b = true;
while ((c == '^') || (c == 'v') || (c == '<') || (c == '>'))
{
if (c == '^')
y++;
else if (c == 'v')
y--;
else if (c == '<')
x--;
else
x++;
for (Point p : visitedHouses) {
if ((p.getAbs() != x) || (p.getOrd() != y))
b = true;
else
b = false;
}
if (b == true) {
visitedHouses.add(new Point(x,y));
}
c = fr.read();
}
System.out.println("Number of houses visited at least once : " + visitedHouses.size());
fr.close();
}
}
我得到“8193”,这是我想的迭代总数,但不是我想要的。 我想知道我至少访问过一次的位置。 我认为问题来自于visitedHouses列表中的for循环。 我想比较当前的x和y是否已存在于此列表中的Point中。我该怎么办?
答案 0 :(得分:1)
当equals
和x
相同时,您需要覆盖Point类中的y
方法以返回true。
像这样的东西:
@Override
public boolean equals(Object other)
{
if (this == other) return true;
if (!(other instanceof Point)) return false;
return (this.x == ((Point)other).x) && (this.y == ((Point)other).y);
}
然后使用Set而不是List。 Set将仅保留唯一的Points。
如果您想继续使用List,请执行以下操作:
if (b == true)
{
Point visitedPoint = new Point(x,y);
if (!visitedHouses.contains(visitedPoint)) {
visitedHouses.add(visitedPoint);
}
}
顺便说一句:你如何识别被访问的房子取决于你......
答案 1 :(得分:1)
代码部分
for (Point p : visitedHouses)
{
if ((p.getAbs() != x) || (p.getOrd() != y))
b = true;
else
b = false;
}
if (b == true)
{
visitedHouses.add(new Point(x,y));
}
看起来特别有问题
如果您在列表中间找到“已访问”的房屋,则下一个条目(希望)不会与该房屋重复,并将“b”标志设置为false,从而导致重复。< / p>
这是由于编程气味“缓存控制变量”。我建议你不要存储控制变量(后来在if语句中检入以改变块的行为的变量。
Point newCandidate = new Point(x,y);
for (Point p : visitedHouses) {
if (newCandidate.getAbs() == p.getAbs() &&
newCandidate.getOrd() == p.getOrd()) {
newCandidate = null;
break;
}
}
if (newCandidate != null) {
visitedHouses.add(newCandidate);
}
但是,这仍然主要是控制变量。如果我们只使用像Set
这样的java HashSet
。
public Point {
... same stuff you have ...
@Override
public int hashcode() {
return 37*x + 23*y + 13;
}
@Override
public boolean equals(Object object) {
if (! object instanceof Point) {
return false;
}
Point other = (Point)object;
if (this.x != other.x) { return false; }
if (this.y != other.y) { return false; }
return true;
}
}
然后你可以做
Set<Point> visitedHouses = new HashSet<Point>();
// possibly initialize the first house
visitedHouses.add(new Point(0, 0));
while (... visiting each house ...) {
Point currentHouse = new Point(currentX, currentY);
visitedHouses.add(currentHouse);
}
并依赖Set
界面确保您永远不会在Point
中获得重复的Collection
条目,因为Set
不能包含重复项(以及您的{{1}现在知道如何获取两个实例并确定它们是否相等。
不仅最后一种方法会更快,因为它存储在Point
中,您无需通过HashSet
中的每个Point
来确定您是否拥有匹配当前位置。 Set
在“用于查询Set的点”上运行,并且通过它的返回值查找避免了99%的其他条目。然后将具有匹配哈希码的小项子集与hashcode(...)
方法进行比较,以确定它们是否等效,或者它们是否“恰好”具有相同的哈希码。
答案 2 :(得分:0)
正如上面的问题评论中所提到的,只要找到重复的点,就需要打破 visitedHouses 循环。
为了提高性能,我在循环中稍微改变了条件,见下文:
boolean found = false;
for (Point p : visitedHouses) {
if ((p.getAbs() == x) && (p.getOrd() == y)) {
found = true;
break;
}
}
if (!found) {
visitedHouses.add(new Point(x,y));
}