红黑树重新平衡问题?

时间:2011-04-28 22:12:17

标签: java collections

compareTo方法是否有任何约束命令将对象放入标准java TreeSet(即实现为红黑树的集合)?我有

public class RuleTuple implements Comparable {
    String head;
    String[] rhs;
    public String toString() {
        StringBuffer b = new StringBuffer();
        b.append(head+":"); 
        for( String t: rhs ) 
           b.append(" "+t); 
        return b.toString();
    }
    public int compareTo(Object obj) {
        RuleTuple src = (RuleTuple)obj;
        int cmp = head.compareTo(src.head);
        if( cmp!=0 )
            return cmp;
        if( rhs.length != src.rhs.length )
            return rhs.length - src.rhs.length;
        for( int i=0; i<rhs.length; i++ )
            if( rhs[i].compareTo(src.rhs[i]) != 0 )
                return rhs[i].compareTo(src.rhs[i]);
        return 0;
    }
    ...
}

我认为任何将对象映射到线性顺序的方法都可以,只要它符合偏序标准:反身性,不对称性和传递性。其中只有传递性并不是立即显而易见的,但在我看来,如果通过排序标准比较对象,则传递性如下。 (我首先比较标题,如果相同,则比较rhs的长度,如果相同,则比较数组的元素。)

显然,RuleTuple.compareTo()方法不一致,因为当删除“test:test [22,33)”时,它按以下顺序遍历树:

test[22,33): 'HAVING' condition               <-- comparison#1
   test: test[4,19) group_by_clause           <-- comparison#2
       test: model_clause                     <-- comparison#3
           test: group_by_clause
              test:
              test: test[22,33)
           test: group_by_clause test[22,33)  <-- comparison#4; wrong branch!
              test: test[4,19)                <-- comparison#5
              test: group_by_clause model_clause
                  ...
       test: test[4,19) group_by_clause model_clause
        ...
   test[4,19): test[5,8) test[8,11)
        ...

因此,它无法找到并删除树上的对象。我的直觉是否正确?

1 个答案:

答案 0 :(得分:6)

Comparator和Comparable的另一个(通常被忽略的)条件是“临时一致性”,即比较两个对象的结果不应该改变,只要它们在TreeMap(或您使用的任何其他结构)中用作键。比较器/可比较,如用于Collections.binarySearch的排序数组,或由最小堆实现的PriorityQueue - 即使对于Arrays.sort,也不应在排序完成之前更改元素。

这实际上意味着你的密钥不应该改变,至少不会改变顺序。

原因是TreeMap假定二叉树的节点总是以正确的顺序 - 只有这样才能在O(log(n))而不是O(n)中进行查找和更改。< / p>

如果您需要更改密钥,则应首先将其从结构中删除,然后进行更改,然后重新添加。

(顺便说一下,对于像HashMap这样的基于散列的结构中的equalshashCode密钥也是如此。)


作为额外的奖励,这里是代码的泛型使用变体:

public class RuleTuple implements Comparable<RuleTuple> {
    String head;
    String[] rhs;
    public String toString() {
        StringBuilder b = new StringBuilder();
        b.append(head+":"); 
        for( String t: rhs ) 
           b.append(" "+t); 
        return b.toString();
    }
    public int compareTo(RuleTuple src) {
        int cmp = head.compareTo(src.head);
        if( cmp!=0 )
            return cmp;
        if( rhs.length != src.rhs.length )
            return rhs.length - src.rhs.length;
        for( int i=0; i<rhs.length; i++ ) {
            int diff = rhs[i].compareTo(src.rhs[i]);
            if(diff != 0)
                return diff;
        }
        return 0;
    }
    ...
}

我还将StringBuffer更改为StringBuilder(这里不使用同步更有效)并且在循环中只使用了一个compareTo。您还可以通过在此处为每个toString运算符使用两个附加内容来优化+方法。