web-dev-qa-db-ja.com

TreeSet内の要素のインデックスを見つける方法は?

私はTreeSet<Integer>を使用していますが、セット内の数値のインデックスを見つけたいと思っています。 O(log(n))バイナリツリーの複雑さを実際に利用するこれを行うための素晴らしい方法はありますか?

(そうでない場合、私は何をすべきか、そして誰かがなぜそうでないのか知っていますか?そのようなクラスがJava検索関数のようなものなしに含まれる理由)に興味があります。)

26
jtbandes

@Yrlecが指摘しているように、セットにこの要素はありませんが、set.headSet(element).sizeは0を返します。したがって、次の点を確認することをお勧めします。

 return set.contains(element)? set.headSet(element).size(): -1;

問題を示すテストケースは次のとおりです。

public static void main(String args[]){
    TreeSet<Integer> set = new TreeSet<>();
    set.add(4);
    set.add(2);
    set.add(3);
    set.add(1);

    System.out.println(set.headSet(1).size());//0
    System.out.println(set.headSet(2).size());//1
    System.out.println(set.headSet(3).size());//2
    System.out.println(set.headSet(4).size());//3
    System.out.println(set.headSet(-1).size());//0!!Caution!,retusn 0 though it does not exits

}
14
Jaskey

しばらくの間、TreeSetとそのインターフェースを試しましたが、要素のインデックスを取得するための最良の方法は次のとおりです。

_set.headSet(element).size()
_

headSet(element)は、引数よりも小さい要素のsub _TreeSetを返すため、このセットのサイズは、問題の要素のインデックスになります。確かに奇妙な解決策です。

47
jtbandes

私も同じ問題を抱えていました。そこで、Java.util.TreeMapのソースコードを使用してIndexedTreeMapと記述しました。それは私自身のIndexedNavigableMapを実装します:

public interface IndexedNavigableMap<K, V> extends NavigableMap<K, V> {
   K exactKey(int index);
   Entry<K, V> exactEntry(int index);
   int keyIndex(K k);
}

実装は、変更されたときに赤黒ツリーのノードの重みを更新することに基づいています。重みは、特定のノードの下にある子ノードの数に1を加えたもの-自己です。たとえば、木が左に回転している場合:

    private void rotateLeft(Entry<K, V> p) {
    if (p != null) {
        Entry<K, V> r = p.right;

        int delta = getWeight(r.left) - getWeight(p.right);
        p.right = r.left;
        p.updateWeight(delta);

        if (r.left != null) {
            r.left.parent = p;
        }

        r.parent = p.parent;


        if (p.parent == null) {
            root = r;
        } else if (p.parent.left == p) {
            delta = getWeight(r) - getWeight(p.parent.left);
            p.parent.left = r;
            p.parent.updateWeight(delta);
        } else {
            delta = getWeight(r) - getWeight(p.parent.right);
            p.parent.right = r;
            p.parent.updateWeight(delta);
        }

        delta = getWeight(p) - getWeight(r.left);
        r.left = p;
        r.updateWeight(delta);

        p.parent = r;
    }
  }

updateWeightは単純にルートまでの重みを更新します。

   void updateWeight(int delta) {
        weight += delta;
        Entry<K, V> p = parent;
        while (p != null) {
            p.weight += delta;
            p = p.parent;
        }
    }

そして、インデックスによって要素を見つける必要があるとき、ここに重みを使用する実装があります:

public K exactKey(int index) {
    if (index < 0 || index > size() - 1) {
        throw new ArrayIndexOutOfBoundsException();
    }
    return getExactKey(root, index);
}

private K getExactKey(Entry<K, V> e, int index) {
    if (e.left == null && index == 0) {
        return e.key;
    }
    if (e.left == null && e.right == null) {
        return e.key;
    }
    if (e.left != null && e.left.weight > index) {
        return getExactKey(e.left, index);
    }
    if (e.left != null && e.left.weight == index) {
        return e.key;
    }
    return getExactKey(e.right, index - (e.left == null ? 0 : e.left.weight) - 1);
}

また、キーのインデックスを見つけるのに非常に便利です:

    public int keyIndex(K key) {
    if (key == null) {
        throw new NullPointerException();
    }
    Entry<K, V> e = getEntry(key);
    if (e == null) {
        throw new NullPointerException();
    }
    if (e == root) {
        return getWeight(e) - getWeight(e.right) - 1;//index to return
    }
    int index = 0;
    int cmp;
    if (e.left != null) {
        index += getWeight(e.left);
    }
    Entry<K, V> p = e.parent;
    // split comparator and comparable paths
    Comparator<? super K> cpr = comparator;
    if (cpr != null) {
        while (p != null) {
            cmp = cpr.compare(key, p.key);
            if (cmp > 0) {
                index += getWeight(p.left) + 1;
            }
            p = p.parent;
        }
    } else {
        Comparable<? super K> k = (Comparable<? super K>) key;
        while (p != null) {
            if (k.compareTo(p.key) > 0) {
                index += getWeight(p.left) + 1;
            }
            p = p.parent;
        }
    }
    return index;
}

間もなくIndexedTreeSetを実装します。その間、IndexedTreeMapのキーセットを使用できます。

Update:IndexedTreeSetが実装されました。

この作業の結果は http://code.google.com/p/indexed-tree-map/ で確認できます。

12

JavaのTreeSetクラスには、セット内の数値のインデックスを見つける機能がありません。そのためには、独自の実装を提供する必要があります-それは内部では赤黒ツリーであり、インデックス操作をサポートするように拡張できます。「アルゴリズムの概要」の「データ構造の拡張」の章にあるOS-RANKプロシージャを見てください。あなたが求めている。

0
Óscar López