【问题标题】:Efficient search for not empty intersection (Java)高效搜索非空交叉点(Java)
【发布时间】:2012-12-10 16:56:13
【问题描述】:

我有一个返回整数值或整数范围(初始..final)的方法,我想知道值是否都是不相交的。

有没有比下面这个更有效的解决方案:

ArrayList<Integer> list = new ArrayList<Integer>();

// For single value
int value;
if(!list.contains(value))
    list.add(value);
else
    error("",null);

// Range
int initialValue,finalValue;
for(int i = initialValue; i <= finalValue; i++){
    if(!list.contains(i))
        list.add(i);
    else
        error("",null);
}

【问题讨论】:

  • error("",null); 似乎没什么用...
  • 范围有多大?如果它们很小,那么将它们作为一系列单独的值处理可能会很有效,但如果它们很大(数百个),那么将它们作为范围处理可能会更好,并将单独的值处理为范围的特殊情况,包含一个值。
  • @BrendanLong 显然,该错误只是一个示例。 Tom Anderson:范围很难预测(这个控件是针对 dsl 的),但我认为它们大约是一两百。

标签: java processing-efficiency disjoint-union


【解决方案1】:

HashSet 中找到一个值 (contains) 平均是一个恒定时间操作 (O(1)),这优于 List,其中 contains 是线性的 (O(n) )。因此,如果您的列表足够大,可能值得将第一行替换为:

HashSet<Integer> list = new HashSet<Integer>();

这样做的原因是,要在(未排序的)列表中查找值,您需要检查列表中的每个索引,直到找到所需的索引或用完要检查的索引。平均而言,如果值在列表中,则在查找值之前,您将检查一半列表,如果不在列表中,则检查整个列表。对于hash table,您从要查找的值生成索引,然后检查该索引(您可能需要check more than one,但在精心设计的哈希表中应该不常见)。

另外,如果你使用 Set,你会得到保证每个值都是唯一的,所以如果你尝试add a value that already exists, add will return false。您可以使用它来稍微简化代码(注意:如果您使用 List,这将不起作用,因为 add always returns true on a List):

HashSet<Integer> list = new HashSet<Integer>();

int value;
if(!list.add(value))
    error("",null);

【讨论】:

    【解决方案2】:

    涉及范围的问题通常适合使用树。这是使用TreeSet 的一种方法:

    public class DisjointChecker {
    
        private final NavigableSet<Integer> integers = new TreeSet<Integer>();
    
        public boolean check(int value) {
            return integers.add(value);
        }
    
        public boolean check(int from, int to) {
            NavigableSet<Integer> range = integers.subSet(from, true, to, true);
            if (range.isEmpty()) {
                addRange(from, to);
                return true;
            }
            else {
                return false;
            }
        }
    
        private void addRange(int from, int to) {
            for (int i = from; i <= to; ++i) {
                integers.add(i);
            }
        }
    
    }
    

    这里,check 方法不是调用错误处理程序,而是返回一个布尔值,指示参数是否与所有先前的参数不相交。范围版本的语义与原始代码中的不同;如果范围不不相交,则不添加任何元素,而在原始范围内,添加第一个不相交元素以下的任何元素。

    有几点可能需要详细说明:

    • Set::add 返回一个布尔值,表示添加是否修改了集合;我们可以将其用作方法的返回值。
    • NavigableSetSortedSet 的一个不起眼但标准的子接口,可悲的是被忽略了。尽管您实际上可以在此处使用简单的SortedSet,只需稍作修改。
    • NavigableSet::subSet 方法(如SortedSet::subSet)返回限制在给定范围内的基础集的轻量级视图。这提供了一种非常有效的方法,可以在一次操作中查询树是否与整个范围重叠。
    • 这里的addRange 方法非常简单,当将m 个项目添加到之前已经查看过n 个项目的检查器时,运行时间为O(m log n)。可以通过编写一个描述整数范围的SortedSet 的实现然后使用Set::addAll 来创建一个在O(m) 中运行的版本,因为TreeSet 的实现包含一个特殊情况在线性时间内添加其他SortedSets。该特殊集合实现的代码非常简单,但涉及大量样板,因此我将其作为练习留给读者!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-27
      • 2012-01-26
      • 1970-01-01
      • 1970-01-01
      • 2015-12-07
      相关资源
      最近更新 更多