web-dev-qa-db-fra.com

Comparaison de ces deux algorithmes?

Donc, je suis confronté à un problème qui énonce. "Déterminer si une chaîne contient tous les caractères uniques"

J'ai donc écrit cette solution qui ajoute chaque caractère à un ensemble, mais si le caractère existe déjà, il retourne faux.

private static boolean allUniqueCharacters(String s) {

    Set<Character> charSet = new HashSet<Character>();
    for (int i = 0; i < s.length(); i++) {
        char currentChar = s.charAt(i);
        if (!charSet.contains(currentChar)) {
            charSet.add(currentChar);

        } else {
            return false;
        }

    }
    return true;

}

D'après le livre que je lis c'est la "solution optimale"

public static boolean isUniqueChars2(String str) {
    if (str.length() > 128)
        return false;

    boolean[] char_set = new boolean[128];

    for (int i = 0; i < str.length(); i++) {
        int val = str.charAt(i);

        if (char_set[val]) {
            return false;
        }
        char_set[val] = true;
    }

    return true;
}

Ma question est la suivante: ma mise en œuvre est-elle plus lente que celle présentée? Je suppose que oui, mais si une recherche par hachage est O(1), ne serait-il pas la même complexité?

Je vous remercie.

18
fsdff

Comme Amadan l'a dit dans les commentaires, les deux solutions ont la même complexité temporelle O(n), car vous avez une boucle for en boucle dans la chaîne et vous effectuez des opérations à temps constant dans la boucle for. Cela signifie que le temps nécessaire à l'exécution de vos méthodes augmente linéairement avec la longueur de la chaîne.

Notez que la complexité temporelle dépend de la manière dont le temps prend change lorsque vous modifiez la taille de l'entrée. Il ne s'agit pas de la rapidité avec des données de la même taille.

Pour la même chaîne, la solution "optimale" devrait être plus rapide, car les ensembles ont des frais généraux par rapport aux tableaux. La manipulation des tableaux est plus rapide que celle des ensembles. Cependant, pour que la solution "optimale" fonctionne, il vous faudrait un tableau de longueur 2 ^ 16. C’est le nombre de valeurs char différentes qui existent. Vous devez également supprimer la vérification d'une chaîne supérieure à 128.

C'est l'un des nombreux exemples de compromis entre l'espace et le temps. Si vous voulez que cela aille plus vite, vous avez besoin de plus d'espace. Si vous voulez économiser de l'espace, vous devez aller plus lentement.

12
Sweeper

Les deux algorithmes ont time complex of O (N). La différence est dans leur espace complexité. 

La solution du livre nécessite toujours un espace de stockage de 128 caractères - O(1), tandis que l'espace requis par votre solution varie linéairement en fonction de l'entrée - O(N).

L'espace requis par le livre est basé sur un jeu de caractères supposé de 128 caractères. Mais cela peut être assez problématique (et non pas évolutif) compte tenu de la probabilité d'avoir besoin de jeux de caractères différents.

3
ernest_k

Le hashmap est en théorie acceptable, mais c'est un gaspillage.

Un hashmap est construit sur un tableau (il est donc certainement plus coûteux qu'un tableau), et la résolution des collisions nécessite un espace supplémentaire (au moins le double du nombre d'éléments). De plus, tout accès nécessite le calcul du hachage et éventuellement la résolution des collisions.

Cela ajoute beaucoup de temps et d’argent, comparé à un tableau simple.

Notez également que c’est une sorte de folklore qu’une table de hachage ait un comportement O(1). Le pire des cas est beaucoup plus pauvre, les accès peuvent prendre jusqu'à O(N) fois pour une table de taille N.


Pour terminer, la complexité temporelle de cet algorithme est O(1), car vous concluez fausse au pire lorsque N> 128.

2
Yves Daoust

Votre algorithme est également O(1). Vous pouvez penser à la complexité comme how my algorithm will react to the change in amount of elements processed. Par conséquent, O(n) et O(2n) sont effectivement égaux. 

Les gens parlent de la notation O comme taux de croissance ici

1
amerykanin

Votre solution est peut-être en effet plus lente que la solution du livre. Premièrement, une recherche de hachage a idéalement une recherche de temps constant. Mais, la récupération de l'objet ne sera pas possible s'il y a plusieurs collisions de hachage. Deuxièmement, même s’il s’agit d’une recherche dans le temps constant, l’exécution de la fonction de code de hachage entraîne généralement une surcharge importante par rapport à la recherche d’un élément dans un tableau par index. C'est pourquoi vous souhaiterez peut-être utiliser la recherche de tableau. Toutefois, si vous commencez à traiter les caractères Unicode non-ASCII, vous pouvez ne pas vouloir utiliser l'approche tableau en raison de la surcharge d'espace.

1
entpnerd

Le goulot d'étranglement de votre implémentation est qu'un ensemble a une complexité * de recherche et d'insertion de O(log k), alors que le tableau a une complexité de recherche dans O(1).

Cela ressemble à votre algorithme doit être bien pire. Mais en fait, ce n’est pas le cas, car k est lié à 128 (sinon l’implémentation de la référence serait fausse et produirait une erreur en dehors des limites) et peut être traitée comme une constante. Cela rend la fonction set lookup O(1) également avec des constantes un peu plus grandes que la table.

* en supposant une implémentation saine sous forme d'arborescence ou de hashmap. La complexité temporelle de la table de hachage est généralement pas constante, son remplissage nécessitant des opérations de redimensionnement de log(n) pour éviter l'augmentation des collisions qui conduiraient à un temps de recherche linéaire, voir par exemple. ici et ici pour obtenir des réponses sur stackoverflow.

Cet article explique même que Java 8 convertit lui-même une table de hachage en une arborescence binaire (O(n log n) pour la conversation, O(log n) pour la recherche) avant que son temps de recherche ne dégénère en O(n) en raison de trop de collisions.

0
allo