考虑这个界面:
public interface CoordinateSet {
boolean contains(int x, int y);
default boolean contains(Coordinate coord) {
return contains(coord.x, coord.y);
}
}
它表示一组二维整数坐标,每个可能的坐标可以在集合内部(contains
返回true
)或外部(contains
返回{{1} })。
我们可以通过多种方式实现这样的界面。计算效率最高的是由数组备份的实现:
false
然而,如果public class ArrayCoordinateSet implements CoordinateSet {
private final boolean[][] coords = new boolean[SIZE][SIZE];
// ...
@Override
public boolean contains(int x, int y) {
return coords[x][y];
}
public void add(int x, int y) {
coords[x][y] = true;
}
// ...
}
是一个很大的东西,比如1000,并且只有4个坐标属于该集合,就在1000×10000矩形的四个角度中,这意味着绝对的大多数SIZE
空间由cells
值消耗。对于这种稀疏的CoordinateSet,我们最好使用基于false
的{{1}}:
HashSet
但是,CoordinateSet
我们遇到了这样的问题:
public final class Coordinate {
public final int x;
public final int y;
public Coordinate(int x, int y) {
this.x = x;
this.y = y;
}
// .equals() and hashCode()
}
public class HashBasedCoordinateSet implements CoordinateSet {
private final Set<Coordinate> coords = new HashSet<>();
@Override
public boolean contains(int x, int y) {
return coords.contains(new Coordinate(x, y));
}
@Override
public boolean contains(Coordinate coord) {
return coords.contains(coord);
}
public void add(Coordinate coord) {
coords.add(coord);
}
}
如果我们有值HashBasedCoordinateSet
和for (int x=0; x<1000; x++) {
for (int y=0; y<1000; y++) {
hashBasedCoordinateSet.contains(x, y);
}
}
并想要检查x
,那么就需要在每次方法调用时创建一个新对象(因为我们总是需要一个对象来搜索在y
中,仅仅拥有对象的数据是不够的。这将浪费CPU时间(它需要创建所有这些hashBasedCoordinateSet.contains(x, y)
个对象,然后对它们进行grabage-collect,因为似乎没有可以对此代码执行转义分析优化。)
最后,我的问题是:
用于存储稀疏坐标集的数据结构是什么:
HashSet
操作; Coordinate
?答案 0 :(得分:2)
long是Java中整数大小的两倍,因此可以在一个long中存储两个int。那怎么样?
public class CoordinateSet {
private HashSet<Long> coordinates = new HashSet<>();
public void add(int x, int y) {
coordinates.add((x | (long) y << 32));
}
public boolean contains(int x, int y) {
return coordinates.contains((x | (long) y << 32));
}
}
我很确定contains方法的长度存储在堆栈中。
答案 1 :(得分:1)
在不进行测量的情况下进行优化当然总是很危险。 您可能应该对您的应用进行分析,看看这是否真的是一个瓶颈。
您还会生成两个用例
通过遍历集合的迭代器并过滤掉您不想要的迭代器,可以提高步骤2的效率。这可能会以任意顺序返回数据。而且性能在很大程度上取决于数据集的大小。
也许一个简单的表数据结构,如Guava提供的那样,可以为您提供更好的界面 - 将X和Y坐标索引为整数 - 同时为您提供O(1)访问。
Table<Integer, Integer, Coordinate> index = HashBasedTable.create();
另一个建议是研究位置敏感的散列。 您基本上创建了一个新的哈希函数,将您的X-Y坐标映射到易于查询的公共一维空间。 但这可能超出了范围。
答案 2 :(得分:1)
如果要使用O(1)数据结构,则需要具有与要在数据结构中存储的实际值无关的查找机制。执行此操作的唯一方法是枚举您的值并派生公式来计算您拥有的对的枚举值,然后为每个枚举值设置一个yes / no值的数组。
例如,如果你保证x保证在0到79之间并且y保证在0到24之间,你可以使用枚举公式y * 80 + x,对于该对(10,10) )将是810.如果为810存储的值为是,则查找非常大的yes / no值数组。
因此,如果您坚持使用O(1)算法,则需要空格来保存是/否值。
答案 3 :(得分:0)
您可以使用构成x和y值的位作为键来尝试二叉树。例如,如果x和y是32位整数,则树的总深度为64.因此,您循环遍历x和y的位,使得最多64个决策到达包含/不包含答案。< / p>
响应评论更新:当然,如果你想要O(1),树不是你通常想到的,但请记住原始问题中基于数组的方法只有O(1)直到可用内存的实现限制。我所做的只是假设整数的位长是一个固定的实现约束,这通常是一个安全的假设。换句话说,如果你真的希望contains()调用在恒定时间内运行,你可以编写它来进行64次比较操作,然后返回。
不可否认,CS教授可能不会购买这个论点。自从我们摆脱了家庭作业标签以来,我很难知道是否有人想要现实世界的答案或理论上的CS答案