2
votes

Trier les éléments du tableau en fonction de leur fréquence

J'ai besoin de trier un tableau d'éléments en fonction de leur fréquence, par exemple:

var set: NSCountedSet = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]

var dictionary = [Int: Int]()
set.forEach { (item) in
    dictionary[item as! Int] = set.count(for: item)
}
dictionary.keys.sorted()
print(dictionary)

J'ai essayé avec le code ci-dessous:

Input array: [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
Expected output: [1, 3, 4, 2, 2, 5, 5, 5, 6, 6, 6, 6]
Description

: comme 1, 3, 4 n'apparaissent qu'une seule fois, ils sont affichés au début, 2 se produisent deux fois, 5 trois fois, 6 quatre fois. Et [1, 3, 4] sont triés parmi eux.

Résultat attendu: La complexité temporelle doit être O(n)^


2 commentaires

Je ne pense pas que vous puissiez arriver à O (n) dans le pire des cas. Si votre exigence est que les éléments uniques soient également triés, cela vous donne déjà O (n * log n).


Le tri par comptage est un choix pour rencontrer o (n) si la fréquence est petite


8 Réponses :


0
votes

Essayez cette solution. Cela a fonctionné pour moi comme un charme :)

func numberOfOccurences(in array: [Int], of element: Int) -> Int {
    let object = NSCountedSet(array: array)
    return object.count(for: element)
}

var inputArray = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]

var uniqueElements = Array(Set(inputArray))

var otherArray: [Int] = []

var duplicateElements = uniqueElements.filter { (element) -> Bool in
    return (inputArray.contains(element) && numberOfOccurences(in: inputArray, of: element) > 1)
}

uniqueElements = uniqueElements.filter({ !duplicateElements.contains($0) }).sorted()

for item in duplicateElements {
    let occurences = numberOfOccurences(in: inputArray, of: item)
    for _ in 0...occurences - 1 {
        otherArray.append(item)
    }
}

otherArray = otherArray.sorted()

duplicateElements.removeAll()

let mergedArray = uniqueElements + otherArray

print(mergedArray)


2 commentaires

Si vous déclarez une condition de boucle de x ... y - 1 , il est plus logique de faire x .. .


Pour l'entrée [1, 1, 1, 2, 2, 3] votre code produit le résultat [3, 1, 1, 1, 2, 2] , qui me semble faux.



3
votes

Vous pouvez essayer

let dd = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
let res = dd.sorted { f, s in
    dd.filter { $0 == f }.count <   dd.filter { $0 == s }.count 
} 
print(res) // [1, 4, 3, 2, 2, 5, 5, 5, 6, 6, 6, 6]


3 commentaires

Je me demande si c'est O (n). - Notez également que le tri n'est pas stable , il n'y a aucune garantie que l'ordre des nombres qui se produisent également souvent est préservé.


@MartinR je ne pense pas qu'un tel problème soit résolu avec en O (n), bien sûr qu'il peut y avoir une autre réponse moins étendue également


Merci pour la réponse @Sh_Khan, mais le résultat attendu doit être [1, 3, 4, 2, 2, 5, 5, 5, 6, 6, 6, 6]



2
votes

Il n'y a aucun moyen de trier avec une complexité en temps O (n). Regardez la complexité du pire des cas pour les algorithmes populaires sur Wikipedia.

Le meilleur pire -La complexité du temps de cas est O (nlogn). Voici comment nous pouvons le résoudre avec une complexité temporelle O (nlogn):

    let array = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]

    extension Array where Element: Comparable & Hashable {
        func countableSorted() -> [Element] {
            var counts = [Element: Int]()
            // O(n)
            for element in self {
                counts[element] = (counts[element] ?? 0) + 1
            }

            // I think that standart method uses O(nlogn) time complexity.
            // O(nlogn) + O(n) approximately equal to O(nlogn).
            let sorted = counts.sorted { item1, item2 -> Bool in
                if item2.value > item1.value {
                    return true
                }

                if item2.value == item1.value {
                    return item2.key > item1.key
                }

                return false
            }

            var result = [Element]()
            // O(n)
            for item in sorted {
                let items = Array(repeating: item.key, count: item.value)
                result.append(contentsOf: items)
            }

            // Total time complexity for worst case scenario is O(nlogn)

            return result
        }
    }

    print(array.countableSorted())

    // Output: [1, 3, 4, 2, 2, 5, 5, 5, 6, 6, 6, 6]


0 commentaires

8
votes

Vous pouvez obtenir les résultats en O (nlogn) temps en créant d'abord un Dictionary contenant le nombre d'occurrences pour chaque élément ( O (n) ), puis appelez sorted sur le Array ( Swift utilise Introsort , qui est O (nlogn) ) et utilise les valeurs de Dictionary précédemment créé pour le tri. Les éléments de votre tableau doivent être Comparables pour que le tri fonctionne et Hashable pour pouvoir les stocker dans un Dictionary , qui fournit O (1) recherche d'élément.

return self.sorted(by: {(occurencesDict[$0]!,$0) < (occurencesDict[$1]!,$1)})

La solution ci-dessus préserve l'ordre des éléments qui se produisent un nombre égal de fois. Si vous souhaitez réellement trier ces éléments en fonction de leurs valeurs comparées (ce que fait votre exemple de sortie), vous pouvez modifier la fermeture dans trié comme ci-dessous:

return self.sorted(by: {occurencesDict[$0]! <= occurencesDict[$1]! && $0 < $1})


9 commentaires

J'allais publier ce let array = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2] let freq = array.reduce (into: [:]) {$ 0 [$ 1, par défaut: 0] + = 1} let sortedArray = array.sorted () {freq [$ 0]!


Cela ne peut pas être fait en O (n). Vous devez d'abord trier ce tableau, puis utiliser la méthode mentionnée ci-dessus.


@RakeshaShastri non, vous n'avez pas besoin de trier d'abord le tableau, c'est fait dans l'appel sorted . Cependant, vous avez raison, la complexité temporelle globale de mon algorithme est O (nlogn) , a mis à jour ma réponse pour refléter cela


trié (par :) n'est pas O (n).


@RobertDresler oui changez simplement le tri en freq [$ 0]! <= fréq [$ 1]! && $ 0 <$ 1


@MartinR vous avez absolument raison, je me suis trompé, a mis à jour ma réponse


@LeoDabus Je pensais faire cela pour la deuxième solution également, mais j'ai utilisé < au lieu de <= , ce qui n'a pas fonctionné, j'ai donc proposé une solution plus longue , mais a mis à jour ma réponse avec votre idée si cela ne vous dérange pas :)


Vous pouvez simplifier le (deuxième) appel de tri en comparant des tuples, comparez par exemple stackoverflow.com/a/37612765


@MartinR merci pour l'idée, j'ai également inclus cette solution dans ma réponse!



1
votes
var inputArray = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
var map:[Int: Int] = [:]
for element in inputArray {
    let count = map[element]
    if count == nil {
        map[element] = 1
    } else {
        map[element] = count! + 1
    }
}
var keysArray = map.keys
let sortedKeys = keysArray.sorted { (number1, number2) -> Bool in
    if map[number1]! == map[number2]! {
        return number1 < number2
    } else {
        return map[number1]! < map[number2]!
    }
}
var finalArray: [Int] = []
for element in sortedKeys {
    for _ in 1...map[element]! {
        finalArray.append(element)
    }
}
print(finalArray)
Time Complexity: O(nlogn)

1 commentaires

@LeoDabus Merci. Édité. En outre, une vérification facultative doit également être effectuée. Mais cela résout le problème fondamental pour nous.



1
votes

Vous pouvez essayer le code ci-dessous, cela a fonctionné correctement.

var inputArray = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
inputArray.sort()
let freq = inputArray.sorted { f, s in
    inputArray.filter { $0 == f}.count < inputArray.filter { $0 == s}.count
}
print(freq)

Je ne suis pas sûr de la complexité temporelle.


0 commentaires

1
votes

Je souhaite ajouter une solution en O (n)

Le tri prend O (nLogn) mais cette question peut également être résolue sans utiliser le tri à l'aide de HashMap en Java car il contient les paires triées selon la clé.

extension Array where Element: Comparable & Hashable {
func sortByNumberOfOccurences() -> [Element] {
    let occurencesDict = self.reduce(into: [Element:Int](), { currentResult, element in
        currentResult[element, default: 0] += 1
    })
    let dict = occurencesDict.sorted(by: {$0.0 < $1.0})
    var dictioanary = [Int:Array]()
    for (element,occurence) in dict {
        if dictioanary[occurence] == nil
        {
            dictioanary[occurence] = Array()
        }
        dictioanary[occurence]?.append(element)
    }


    var resultArray = Array()
    let finalDict = dictioanary.sorted(by: {$0.0  < $1.0})
    for (frequency,allValuesOccuringWithThisFrequncy) in finalDict {
       for i in allValuesOccuringWithThisFrequncy
       {
        var j = 0
        while(j < frequency)
        {
            resultArray.append(i)
            j = j + 1
        }
       }
    }
    print(resultArray)
    return resultArray
}
  1. Dans la boucle First for, je suis en train d'itérer à travers une paire de sauvegarde de tableau de (valeur, occurrence) dans map1 (HashMap). Cela prendra O (n) comme l'opération put (insertion) HashMap prend O (1).
  2. Dans la deuxième boucle for, j'itère map1 et j'insère une paire de (occurrence, liste de nombres dans le tableau donné avec cette occurrence) dans map2 (HashMap2).
  3. Maintenant, dans la dernière boucle for, j'itère dans map2 et j'imprime toutes les listes une par une, cela signifie que j'imprime chaque élément du tableau donné une fois, c'est-à-dire que je suis en train d'itérer la liste de chaque clé et d'imprimer chaque élément de la liste key nombre de fois. Donc, cela prendrait également O (n).

en savoir plus sur HashMap Complexité temporelle: O (n)

Version Swift du code ci-dessus

import java.util.*; 

class Simple 
{ 
    public static void main(String[] arg) 
    {  int inputArray[] = {1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2};
        Map<Integer,Integer> map = new HashMap<Integer,Integer>(); 
        Map<Integer,List<Integer>> map2 = new HashMap<Integer,List<Integer>>();
       for(int i: inputArray)
      {
                  if(map.get(i) == null){
                 map.put(i, 1) ;
                  }
                  else{
                  int a = map.get(i);
                  map.put(i,a+1);
                 }
      }

        // using for-each loop for iteration over Map.entrySet() 
        for (Map.Entry<Integer,Integer> entry : map.entrySet()) {
            if(map2.get(entry.getValue()) == null){
                map2.put(entry.getValue(), new ArrayList<Integer>()) ;
            }
            map2.get(entry.getValue()).add(entry.getKey());
        }

        for(Map.Entry<Integer,List<Integer>> entry : map2.entrySet()){
            for(int j=0; j<entry.getValue().size(); j++){
                for(int i=0; i<entry.getKey(); i++){
                System.out.print(entry.getValue().get(j) + " ");
            }
            }

        }    
    }         

}

}

Complexité temporelle dans Swift O (nLogn)


2 commentaires

Pouvez-vous essayer la même approche dans Swift?


@Bappaditya Salut, j'ai essayé d'écrire le code en swift. J'ajoute le code. Mais comme Dictionary in Swift ne maintient pas l'ordre, nous devons appliquer la méthode de tri à cela et cela rend la complexité O (nLogn). Mais en java HashMap, insérez les paires de manière triée sur la base de la clé. J'aurais aimé que nous ayons un type de collection similaire dans Swift aussi. Triste: (Je pense que nous devons écrire notre propre HashMap en Swift: P



0
votes

Je pense que ce type de tri peut être réalisé en O (n), avec quelque chose comme ce qui suit:

[[4, 3, 1], [2, 2], [5, 5, 5], [6, 6, 6, 6], [], [], [], [], [], [], [], []]

Exemple de résultat:

[4, 1, 3, 2, 2, 5, 5, 5, 6, 6, 6, 6]

Notez que l'ordre des nombres avec la même fréquence peut différer en fonction du fonctionnement de la fonction de hachage du dictionnaire.

Nous sacrifions également l'espace (tableau 2-D alloué) au profit du temps. p >

Le contenu de frequencyTable ressemblera à ceci (encore une fois, l'ordre de 1, 4, 3 peut différer):

let input = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]

// build the frequency dictionary (easy task)
let frequencies = input.reduce(into: [:]) { $0[$1] = ($0[$1] ?? 0) + 1 }

// allocate a 2-D array of ints, each item in this array will hold the numbers that
// appear I times in the input array
let frequencyTable: [[Int]] = frequencies.reduce(into: Array(repeating: [Int](), count: input.count)) {
    // substracting one as arrays are zero indexed
    // we can't get of of bounds here since the maximum frequency is the 
    // number of elements in the input array
    // Also replicating the numbers by their frequency, to have
    // the same contents as in the input array
    $0[$1.value - 1] += Array(repeating: $1.key, count: $1.value)
}

// lastly, simply flatten the 2-D array to get the output we need
let output = frequencyTable.flatMap { $0 }

print(output)


1 commentaires

Autrement connu sous le nom de Tri par comptage