web-dev-qa-db-fra.com

Comment retourner le kth élément dans TreeSet en Java

Peut-être que je n'utilise pas la bonne structure de données. Je dois utiliser un ensemble, mais je veux aussi retourner efficacement le kème plus petit élément. TreeSet en Java peut-il le faire? Il semble qu'aucune méthode intégrée de TreeSet ne le fasse.

Aidez-moi, s'il vous plaît.

18
user1096734

Je ne crois pas que TreeSet ait une méthode qui le fasse directement. Il existe des arbres de recherche binaires qui prennent en charge l'accès aléatoire O (log n) (ils sont parfois appelés arbres de statistiques order), et il existe des implémentations Java de cette structure de données available. Ces structures sont généralement implémentées sous forme d’arbres de recherche binaires qui stockent des informations dans chaque nœud en comptant le nombre d’éléments situés à gauche ou à droite du nœud. Vous pouvez donc effectuer une recherche dans l’arborescence pour rechercher l’élément approprié en descendant dans le sous-arbre approprié. chaque étape. Le livre classique "Introduction aux algorithmes, troisième édition" de Cormen, Rivest, Leisserson et Stein explore cette structure de données dans le chapitre "Augmenter les structures de données" si vous êtes curieux de savoir comment vous pouvez en implémenter une.

Dans certains cas, vous pouvez également utiliser la méthode TreeSet 's tailSet et une recherche binaire modifiée pour essayer de trouver le kth élément. Plus précisément, examinez les premier et dernier éléments de la variable TreeSet, puis (si possible, en fonction du contenu) choisissez un élément situé à mi-chemin et transmettez-le comme argument à tailSet pour obtenir une vue des éléments de l'ensemble après la fin. point médian. En utilisant le nombre d'éléments dans la variable tailSet, vous pourrez ensuite décider si vous avez trouvé l'élément ou s'il faut explorer les moitiés gauche ou droite de l'arbre. Ceci est une recherche légèrement modifiée d'interpolation sur l'arbre, et pourrait être rapide. Cependant, je ne connais pas la complexité interne des méthodes tailSet. Cela pourrait donc être pire que l'arborescence de statistiques d'ordre. Cela peut également échouer si vous ne pouvez pas calculer le "point milieu" de deux éléments, par exemple si vous stockez Strings dans votre TreeSet.

J'espère que cela t'aides!

19
templatetypedef

Il vous suffit de parcourir l'élément k. Une façon de le faire serait d’utiliser l’une des méthodes Guava 's Iterables.get :

T element = Iterables.get(set, k);

Il n'y a pas de méthode intégrée pour le faire car une Set n'est pas une List et les opérations basées sur un index comme celles-ci sont généralement réservées à Lists. Une TreeSet est plus appropriée pour des choses telles que trouver l'élément contenu le plus proche qui est> = une valeur.

Une chose que vous pourriez faire si l'accès le plus rapide possible au kème élément le plus petit était vraiment important serait d'utiliser une variable ArrayList plutôt qu'une TreeSet et de gérer les insertions en recherchant binaire le point d'insertion cet index ou en remplaçant l'élément existant à cet index, en fonction du résultat de la recherche. Ensuite, vous pourriez obtenir le k-ième élément le plus petit dans O(1) en appelant simplement get(k).

Vous pouvez même créer une implémentation de SortedSet qui gère tout cela et ajoute la méthode get(index) si vous le souhaitez vraiment.

18
ColinD

Utilisez TreeSet.iterator () pour obtenir un itérateur par ordre croissant et appelez next() K fois:

// Example for Integers
Iterator<Integer> it = treeSet.iterator();
int i = 0;
Integer current = null;
while(it.hasNext() && i < k) {
   current = it.next();
   i++;
}
6
Tudor

J'ai eu le même problème. J'ai donc pris le code source de Java.util.TreeMap et écrit IndexedTreeMap. Il implémente mon propre IndexedNavigableMap:

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

L'implémentation est basée sur la mise à jour des poids de nœud dans l'arborescence rouge-noir lors de sa modification. Weight est le nombre de nœuds enfants situés sous un nœud donné, plus un - self. Par exemple, lorsque vous faites pivoter un arbre vers la gauche:

    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 met simplement à jour les poids à la racine:

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

Et lorsque nous avons besoin de trouver l'élément par index, voici l'implémentation qui utilise des poids:

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);
}

Il est également très utile de trouver l’index d’une clé:

    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;
}

Vous pouvez trouver le résultat de ce travail à l'adresse http://code.google.com/p/indexed-tree-map/ Déplacé vers https://github.com/geniot/indexed-tree-map

6
Vitaly Sazanovich

[Ci-dessous, je résume "la kème opération de recherche du plus petit élément" par "Kth op."]

Vous devez donner plus de détails. Quelles opérations votre structure de données fournira-t-elle? l’opération K dans Kth est-elle très petite comparée à N, ou peut-il être autre chose? À quelle fréquence ferez-vous des insertions et des suppressions par rapport aux recherches? À quelle fréquence aurez-vous Kth plus petite recherche d'éléments par rapport aux recherches? Êtes-vous à la recherche d'une solution rapide de quelques lignes dans la bibliothèque Java ou souhaitez-vous consacrer des efforts à la création d'une structure de données personnalisée? 

Les opérations à fournir peuvent être n’importe quel sous-ensemble de:

  • LookUp (trouve un élément par sa clé; où la clé est comparable et peut être n'importe quoi)

  • _ {Insérer} _

  • Effacer

  • Kth 

Voici quelques possibilités:

  • S'il n'y aura pas/très peu d'insertions et de suppressions, vous pouvez simplement trier les éléments et utiliser un tableau, avec O(Log(N)) le temps de recherche et O(1) pour Kth.

  • Si O(Log(N)) pour Recherche, Insertion, Supprimer et O(k) pour Kth op. est assez bon, probablement la mise en œuvre la plus facile serait Ignorer les listes. (L'article Wikipedia est très bon si vous avez besoin de plus de détails)

  • Si K est assez petit ou Kth les opérations ne viendront qu'après la "phase d'insertion et de suppression", vous pouvez conserver les plus petits éléments K d'un segment de mémoire, tri après les insertions et suppressions pour O (N + k Log k) temps. (Vous aurez également besoin d'un hachage séparé pour LookUp _)

  • Si K est arbitraire et que O(N) est suffisant pour l'opération Kth, vous pouvez utiliser un hachage pour O(1) la recherche temporelle et utilisez un algorithme "QuickSort à un côté" pour les opérations Kth (l'idée de base est de faire un tri rapide mais sur chaque ne divisez que le côté dont vous avez réellement besoin, ce qui donnerait (c'est une simplification grossière) N (1/2 + 1/4 + 1/8 + ...) = O(N) heure prévue)

  • Vous pouvez créer une arborescence d'intervalle "simple" augmentée, chaque nœud conservant le nombre de ses enfants, de sorte que LookUp, Insertion, Supprimer, Kth tous calculent en O (Log N)} tant que l'arbre est équilibré, mais il ne serait peut-être pas difficile de l'implémenter si vous êtes novice.

etc. etc. L'ensemble des alternatives est infini, tout comme les interprétations possibles de votre question.

1
Ali Ferhat

Pourriez-vous utiliser un ConcurrentSkipListSet et utiliser la méthode toArray ()? ConcurrentSkipListSet est trié selon l'ordre naturel des éléments. La seule chose dont je ne suis pas sûr est si toArray () est O(n) ou s'il est sauvegardé par une liste (sauvegardée par un tableau, comme ArrayList), il s'agit de O (1).

Si toArray () est O(1), vous devriez pouvoir être un skipList.toArray () [k] pour obtenir le k-ème plus petit élément.

0
Justin