Algorithme pour trouver le plus petit entier en échangeant une paire de chiffres dans un entier donné

Étant Donné un entier positif (sous la forme d'un tableau de chiffres). Nous sommes autorisés à échanger une paire de chiffres dans le nombre donné. Nous devons retourner le plus petit entier possible qui peut être obtenu. Notez qu'il doit s'agir d'un entier valide, c'est-à-dire qu'il ne doit pas contenir de 0.

Par exemple:-

  1. 93561 renvoie 13569
  2. 596 renvoie 569
  3. 10234 renvoie 10234
  4. 120 renvoie 102
  5. 10091 renvoie 10019
  6. 98761111 renvoie 18761119

Existe-t-il un algorithme O(n) pour ce problème. J'ai pensé à quelques façons pour cela: -

  1. trouver le min. chiffre (minDIgit) dans l'entier donné (sauf 0) et l'échanger avec le MSB, si MSB != minDigit. Si MSB==minDigit, alors trouvez la minute suivante. digit et échangez-le avec le chiffre le plus significatif mais 1 et ainsi de suite. Cela pourrait être O(n^2) dans le pire des cas.
  2. faites un array/vector de std::pair de digit et index et triez - le dans l'ordre croissant (selon les valeurs de chiffres; gardez inférieur indices d'abord pour les valeurs de chiffres correspondants). Parcourir le tableau trié. Échangez L'ESM avec le premier chiffre. Si le chiffre le moins a un index correspondant en tant que MSB, échangez le MSB mais 1 place avec le chiffre min suivant. Si le chiffre min suivant a un index correspondant DE MSB mais 1 place, alors échangez le MSB mais 2 place avec ce min suivant. chiffres et ainsi de suite. Cela devrait être O(nlog(n)).

Quelqu'un Peut-il suggérer un meilleur algorithme.


Mise à jour 1: Après avoir réfléchi un peu, deuxième algo que j'ai proposé fonctionnerait parfaitement bien (probablement sauf quelques cas de coin, qui peuvent être traités séparément). De plus, je peux trier la paire(chiffre, index) en utilisant tri de comptage (selon les valeurs de chiffres), qui est un tri stable dans O(n) Temps. Est-il une faille dans mon raisonnement?


Mise à jour 2: Mon 2nd algo fonctionnerait (bien qu'avec plus de vérifications pour les cas de coin et les 0) et cela aussi dans O(n) Temps avec counting sort. Mais la solution donnée par @GaborSch est beaucoup plus simple, donc je ne vais pas vraiment donner un code approprié pour mon algo.

24
demandé sur Shobhit 2013-06-18 20:23:12

6 réponses

En préparation, nous faisons une boucle à travers les chiffres, et marquons lesdernières positions des chiffres dans un tableau[10] (appelez-le last) (y compris 0 s). Qui est O(n).

Ensuite, nous commençons à parcourir les chiffres de gauche à droite. Pour chaque position, nous essayons de trouver le plus petit chiffre dont la position dernière est supérieure à notre position actuelle (contrainte de position). En outre, ce chiffre doit être plus petit que le chiffre actuel.

Si nous sommes dans la première position, nous commençons la boucle sur last à partir de 1 (sinon à partir de 0), juste jusqu'à la valeur du chiffre courant (non compris).

Si nous trouvons un tel chiffre (concernant la contrainte de position), nous échangeons (et cassons la boucle). Si nous ne le faisons pas, nous passons au chiffre suivant. Le coût est au plus O(n*9), qui est O(n).

, Le coût total est O(n) + O(n)*S(9) = O(n).

Comment fonctionne l'algorithme de travail sur les exemples:

93561 ->   it can swap in the first cycle

596   ->   skipping the first cycle, 
           then finds '6' because of the position constraint 
           (comparing position '1' with last[5] = 0, last[6] = 2)

10234 ->   does not find anything because of the position constraint

93218910471211292416 -> finds the last occurrence of '1' to replace '9'

98761111 -> it can swap in the first cycle
            (last[1] = 7, so it will change the last occurrence)

555555555555555555596 -> iterates until the '9', then you skip last[5]
            but finds last[6] as a good swap

120 ->      at pos 0 (1) cannot find a non-zero element less than 1, so skip
            at pos 1 (2) can find 0 on position 2, so we swap

Encore une fois, ici nous faisons une itération sur les chiffres (pour pré-analyser les données), puis une itération pour trouver le MSB. Dans la deuxième itération, nous itérons sur last, qui est de taille constante (au plus 9).

Vous pouvez optimiser davantage l'algorithme en ajoutant garder une trace de la valeur minimale quand il vaut la peine de démarrer la boucle sur last, mais c'est déjà une optimisation. La version prevoius contenait cela, vérifiez l'historique si vous êtes intéressé:)

17
répondu gaborsch 2013-06-19 00:51:51

Comptez D'abord chaque chiffre, stockez-le dans un tableau (counts[10]).

En partant de la gauche, vérifiez les chiffres (voici la description de la boucle):

Vérifiez qu'il y a un chiffre dans counts qui est plus petit que lui. Choisissez le plus petit. Exception: 0 n'est pas autorisé pour le tout premier chiffre.

  • S'il y en a un, échangez, vous avez terminé (quittez la boucle!).
  • sinon, décrémentez le chiffre dans counts, et passez au chiffre suivant.

Pour chaque chiffre que vous faites O (1) travail. Ainsi, l'ensemble de algo en O(n).

Pour l'échange, vous voulez utiliser les chiffres les moins significatifs (plus à droite). Vous pouvez soit stocker ces emplacements sur la recherche initiale, ou juste avant l'échange, vous pouvez rechercher le premier chiffre correspondant à partir de la fin.

10
répondu Karoly Horvath 2013-06-18 22:19:26

Je voudrais parcourir le tableau en commençant à droite. Stockez le chiffre sur la droite comme le plus petit chiffre et le chiffre maximum et commencez à bouger à gauche, si vous frappez un nouveau plus petit nombre, appelez-le le plus petit potentiel. Si vous continuez à avancer à gauche et que vous trouvez un plus petit nombre, faites du plus petit le potentiel. Si vous trouvez un plus grand nombre, faites un plus petit potentiel le plus petit int et stockez le plus grand en tant que chiffre maximum. Chaque fois que vous frappez un chiffre plus grand que votre plus petit, faites-en le nouveau chiffre maximum. Si vous appuyez sur la fin, swap max digit et le plus petit chiffre. En python (cela fonctionne et est O (n))

def swap_max(digits):
    i = len(digits) - 1
    while i > 0:
        if digits[i] == 0:
            i-= 1
        else:
            break
    max_i = i
    min_i = i
    pot_i = i
    z_i   = -1
    nz_i  = i
    i = len(digits) - 1
    while i >= 0:
        if digits[i] > digits[pot_i]:
            max_i = i
            min_i = pot_i
        if digits[i] < digits[min_i] and digits[i] != 0:
            pot_i = i
        if digits[i] == 0 and z_i == -1:
            z_i = i
        if digits[i] != 0 and i > 0:
            nz_i = i
        i -= 1
    if z_i != -1 and max_i != 0 and max_i < z_i:
        min_i = z_i
        i = nz_i
        max_i = i
    elif max_i == min_i and z_i != -1:
        i = nz_i
        if i < z_i:
            min_i = z_i
            max_i = i

    v = digits[min_i]
    digits[min_i] = digits[max_i]
    digits[max_i] = v
    return digits


#TESTING THE FUNCTION
tests =   [93561,596,10234,120,10091,98761111,1001,1010,1103,120,93218910471211292416]
results = [13569,569,10234,102,10019,18761119,1001,1001,1013,102,13218910471211292496]
tests = map(list,map(str,tests))
results = map(list,map(str,results))
for i in range(len(tests)):
    res ="".join(map(str,swap_max(map(int,tests[i]))))
    print res,"".join(results[i])
    if res=="".join(results[i]):
        print "PASSED\n"
    else:
        print "FAILED\n"

Cela a fini par fonctionner pour tous les exemples. Il a également l'avantage D'être O(1) utilisation de la mémoire.

5
répondu Triclops200 2013-06-19 12:19:18

Voici un algorithme simple O(n):

- Record 'false' for each of ten digit values, 0 through 9
- Work through the number's digits, left-to-right
    - If the digit value is associated with 'true' go to the next digit and continue
    - Record 'true' for this digit
    - Search all the digits to the right for the right-most, smallest digit
      (except zero for the first digit in the number)
      and swap if the lowest digit found (if any) is less than the current digit
    - If swapped, report success and stop
    - If not swapped, go to the next digit and continue
- If we reach the end of the digit list, report a lack of success and stop

Cela peut ne pas sembler être O (n) lors de la première inspection, mais après avoir réalisé que la boucle interne ne peut pas être exécutée plus de dix fois, il devient évident que c'est O(n) depuis O(n - 10 + 10*n) = O(11*n - 10) = O(n).

2
répondu Kaganar 2013-06-18 21:18:46

PseudoCode : O(n)

1) divisez le nombre en chiffres individuels, disons le chiffre [10] (comme indiqué dans une autre réponse). Init incPos = -1.

2) traverser à partir du chiffre le plus à droite, pour trouver le plus à gauche increasingPos (incPos). c'est-à-dire en traversant , comparez K+1 élément avec le kème élément. Pour, chaque chiffre[k] ≠ 0, Si digit[k] >= digit[k+1], puis marque incPos comme k. Traversez jusqu'à gauche le plus et trouvez le le moins incPos.

4) Si incPos = = -1 renvoie num, sinon traversez la forme incPos à n pour trouver le chiffre Right-Most-Minimum (comme décrit dans le bloc ci-dessous), swap avec le chiffre le plus à droite et le retour. (sûrement il y aura au moins 1 chiffre.)

E.g  
93561 ->                IncPos = 0,  Right most minimum : 1 at pos 4 
596   ->                IncPos = 1,  Right most minimum : 6 at pos 2 
10234 ->                IncPos = -1, return 10234  
93218910471211292416 -> IncPos = 0,  Right most minimum : 1 at pos 18 
98761111 ->             IncPos = 0,  Right most minimum : 1 at pos 7 

5) formez le nombre avec de nouveaux chiffres. Numéro de retour.

1
répondu Muthu Ganapathy Nathan 2013-06-19 03:28:44

Légère variation de L'algorithme de Karoly Horvath

Vous pouvez trier le tableau de chiffres dans O(N).

Maintenant, nous avons 2 listes triées et réelle. Réel est notre tableau d'origine.

Itérer sur réel de gauche à droite,

Pour chaque valeur, Pop off éléments de triés jusqu'à ce que nous atteignons une valeur dont la position dans le tableau d'origine est

Si la tête de la valeur de la liste triée est

Le tri a été effectué en temps O (n). Tout au plus nous sautons n éléments du total de la liste triée, et nous itérons sur la liste d'origine une seule fois, de sorte que l'algorithme entier devrait être O(n) dans le temps et l'espace.

Bien sûr, il y a une vérification de cas particulier pour un échange de 0 avec l'élément le plus à gauche, mais cela n'affecte pas la complexité.

0
répondu AndyG 2013-06-18 17:22:08