web-dev-qa-db-fra.com

Obtenir le deuxième plus grand nombre dans une liste en temps linéaire

J'apprends le python et les méthodes simples de gestion des listes sont présentées comme un avantage. Parfois c'est le cas, mais regardez ceci:

>>> numbers = [20,67,3,2.6,7,74,2.8,90.8,52.8,4,3,2,5,7]
>>> numbers.remove(max(numbers))
>>> max(numbers)
74

Un moyen très facile et rapide d’obtenir le deuxième plus grand nombre à partir d’une liste. Sauf que le traitement facile des listes permet d'écrire un programme qui parcourt la liste deux fois, pour trouver le plus grand, puis le deuxième. C'est également destructeur: j'ai besoin de deux copies des données si je veux conserver l'original. Nous avons besoin:

>>> numbers = [20,67,3,2.6,7,74,2.8,90.8,52.8,4,3,2,5,7]
>>> if numbers[0]>numbers[1]):
...    m, m2 = numbers[0], numbers[1]
... else:
...    m, m2 = numbers[1], numbers[0]
...
>>> for x in numbers[2:]:
...    if x>m2:
...       if x>m:
...          m2, m = m, x
...       else:
...          m2 = x
...
>>> m2
74

Ce qui ne parcourt la liste qu'une seule fois, mais n'est pas concis et clair comme dans la solution précédente.

Alors: y a-t-il un moyen, dans des cas comme celui-ci, d’avoir les deux? La clarté de la première version, mais le single de la seconde?

33
boisvert

Puisque @OscarLopez et moi-même avons des opinions différentes sur ce que signifie le deuxième plus gros, je posterai le code conformément à ma vision et conformément au premier algorithme fourni par le demandeur.

def second_largest(numbers):
    count = 0
    m1 = m2 = float('-inf')
    for x in numbers:
        count += 1
        if x > m2:
            if x >= m1:
                m1, m2 = x, m1            
            else:
                m2 = x
    return m2 if count >= 2 else None

(Remarque: l'infini négatif est utilisé ici au lieu de None puisque None a un comportement de tri différent en Python 2 et 3 - voir Python - Trouver le deuxième plus petit nombre ; un contrôle du nombre d'éléments dans numbers permet de s'assurer que l'infini négatif a gagné ne sera pas renvoyé lorsque la réponse réelle sera indéfinie.)

Si le maximum se produit plusieurs fois, il se peut également que ce soit le deuxième plus grand. Une autre chose à propos de cette approche est que cela fonctionne correctement s'il y a moins de deux éléments; alors il n'y a pas de deuxième plus grand.

Effectuer les mêmes tests:

second_largest([20,67,3,2.6,7,74,2.8,90.8,52.8,4,3,2,5,7])
=> 74
second_largest([1,1,1,1,1,2])
=> 1
second_largest([2,2,2,2,2,1])
=> 2
second_largest([10,7,10])
=> 10
second_largest([1,1,1,1,1,1])
=> 1
second_largest([1])
=> None
second_largest([])
=> None

Mettre à jour

J'ai restructuré les conditionnels pour améliorer considérablement les performances. presque par 100% dans mes tests sur des nombres aléatoires. La raison en est que, dans la version d'origine, la Elif était toujours évaluée dans l'éventualité probable que le nombre suivant ne soit pas le plus grand de la liste. En d’autres termes, pour pratiquement chaque nombre de la liste, deux comparaisons ont été effectuées, alors qu’une comparaison suffit généralement - si le nombre n’est pas plus grand que le deuxième plus grand, il n’est pas plus grand que le plus grand.

23
Thijs van Dien

Vous pouvez utiliser le module heapq :

>>> el = [20,67,3,2.6,7,74,2.8,90.8,52.8,4,3,2,5,7]
>>> import heapq
>>> heapq.nlargest(2, el)
[90.8, 74]

Et à partir de là ...

47
Jon Clements

Vous pouvez toujours utiliser sorted

>>> sorted(numbers)[-2]
74
16
Volatility

Essayez la solution ci-dessous, elle est O(n) et il stockera et retournera le deuxième plus grand nombre dans la variable second. Notez que si tous les éléments de numbers sont égaux, ou si numbers est vide ou s'il contient un seul élément, la variable second se retrouvera avec une valeur de None - ceci est correct, car il n'y a pas de "seconde plus grand "élément.

Attention : ceci trouve la valeur "deuxième maximum". S'il y a plus d'une valeur qui est "premier maximum", elles seront toutes traitées comme le même maximum - dans ma définition, dans une liste telle que celle-ci: [10, 7, 10] la bonne réponse est 7.

def second_largest(numbers):
    first, second = None, None
    for n in numbers:
        if n > first:
            first, second = n, first
        Elif first > n > second:
            second = n
    return second

Voici quelques tests:

second_largest([20,67,3,2.6,7,74,2.8,90.8,52.8,4,3,2,5,7])
=> 74
second_largest([1,1,1,1,1,2])
=> 1
second_largest([2,2,2,2,2,1])
=> 1
second_largest([10, 7, 10])
=> 7
second_largest([1,1,1,1,1,1])
=> None
second_largest([1])
=> None
second_largest([])
=> None
11
Óscar López
>>> l = [19, 1, 2, 3, 4, 20, 20]
>>> sorted(set(l))[-2]
19
3
Brent D.

L'algorithme quickselect , O(n) cousin de quicksort, fera ce que vous voulez. Quickselect a une performance moyenne O (n). Les performances dans le pire des cas sont O (n ^ 2), tout comme le tri rapide, mais c'est rare, et des modifications apportées à quickselect réduisent les performances dans le pire des cas à O (n).

L'idée de quickselect est d'utiliser le même pivot, une idée plus basse et plus élevée du tri rapide, mais d'ignorer ensuite la partie inférieure et d'ordonner davantage la partie supérieure.

3
Edward Doolittle

Pourquoi compliquer le scénario? C'est très simple et direct 

  1. Convertir la liste en fichiers - supprime les doublons
  2. Convertir à nouveau la liste - qui donne la liste par ordre croissant

Voici un code

mlist = [2, 3, 6, 6, 5]
mlist = list(set(mlist))
print mlist[-2]
3

Si cela ne vous dérange pas d'utiliser numpy (import numpy as np):

np.partition(numbers, -2)[-2]

vous donne le 2e élément le plus volumineux de la liste avec le pire cas garanti O(n) temps d'exécution

Les méthodes partition(a, kth) renvoient un tableau dans lequel l'élément kth est identique à celui qu'il aurait dans un tableau trié, tous les éléments précédents sont plus petits et tous les éléments suivants sont plus grands.

2
serv-inc

il y a de bonnes réponses ici pour le type ([]), au cas où quelqu'un aurait besoin de la même chose sur un type ({}), c'est ici, 

def secondLargest(D):
    def second_largest(L):  
        if(len(L)<2):
            raise Exception("Second_Of_One")
        KFL=None #KeyForLargest
        KFS=None #KeyForSecondLargest
        n = 0
        for k in L:
            if(KFL == None or k>=L[KFL]):
                KFS = KFL
                KFL = n
            Elif(KFS == None or k>=L[KFS]):
                KFS = n
            n+=1
        return (KFS)
    KFL=None #KeyForLargest
    KFS=None #KeyForSecondLargest
    if(len(D)<2):
        raise Exception("Second_Of_One")
    if(type(D)!=type({})):
        if(type(D)==type([])):
            return(second_largest(D))
        else:
            raise Exception("TypeError")
    else:
        for k in D:
            if(KFL == None or D[k]>=D[KFL]):
                KFS = KFL               
                KFL = k
            Elif(KFS == None or D[k] >= D[KFS]):
                KFS = k
    return(KFS)

a = {'one':1 , 'two': 2 , 'thirty':30}
b = [30,1,2] 
print(a[secondLargest(a)])
print(b[secondLargest(b)])

Juste pour le plaisir, j'ai essayé de le rendre convivial xD

2
kpie

O (n): La complexité temporelle d'une boucle est considérée comme O(n) si les variables de la boucle sont incrémentées/décrémentées d'une quantité constante. Par exemple, les fonctions suivantes ont une complexité temporelle de O(n).

 // Here c is a positive integer constant   
   for (int i = 1; i <= n; i += c) {  
        // some O(1) expressions
   }

Pour trouver le deuxième plus grand nombre, j’ai utilisé la méthode ci-dessous pour trouver d’abord le plus grand nombre, puis rechercher dans la liste si c’est là ou non. 

x = [1,2,3]
A = list(map(int, x))
y = max(A)
k1 = list()
for values in range(len(A)):
if y !=A[values]:
    k.append(A[values])

z = max(k1)
print z
1
Bharatwaja

Cela peut être fait en temps [N + log (N) - 2], ce qui est légèrement meilleur que la limite supérieure libre de 2N (ce qui peut aussi être pensé à O(N)). 

L'astuce consiste à utiliser des appels binaires récursifs et l'algorithme "tournoi de tennis". Le gagnant (le plus grand nombre) apparaîtra après tous les «matchs» (prend N-1 fois), mais si nous enregistrons les «joueurs» de tous les matchs, et parmi eux, regroupez tous les joueurs que le gagnant a battus, le deuxième plus grand nombre sera le plus grand nombre de ce groupe, c’est-à-dire le groupe des «perdants». 

La taille de ce groupe de «perdants» est log (N) et, encore une fois, nous pouvons révoquer les appels binaires récursifs pour trouver le plus grand des perdants, ce qui prendra du temps [log (N) - 1]. En fait, nous pouvons simplement balayer linéairement le groupe des perdants pour obtenir la réponse également, le budget-temps est le même. 

Voici un exemple de code python:

def largest(L):
    global paris
    if len(L) == 1:
        return L[0]
    else:
        left = largest(L[:len(L)//2])
        right = largest(L[len(L)//2:])
        pairs.append((left, right))
        return max(left, right)

def second_largest(L):
    global pairs
    biggest = largest(L)
    second_L = [min(item) for item in pairs if biggest in item]

    return biggest, largest(second_L)  



if __== "__main__":
    pairs = []
    # test array
    L = [2,-2,10,5,4,3,1,2,90,-98,53,45,23,56,432]    

    if len(L) == 0:
        first, second = None, None
    Elif len(L) == 1:
        first, second = L[0], None
    else:
        first, second = second_largest(L)

    print('The largest number is: ' + str(first))
    print('The 2nd largest number is: ' + str(second))
1
ccy
    def SecondLargest(x):
        largest = max(x[0],x[1])
        largest2 = min(x[0],x[1])
        for item in x:
            if item > largest:
               largest2 = largest
               largest = item
            Elif largest2 < item and item < largest:
               largest2 = item
        return largest2
    SecondLargest([20,67,3,2.6,7,74,2.8,90.8,52.8,4,3,2,5,7])
1
Aysun

Vous pouvez trouver le 2ème plus grand de l'une des manières suivantes:

Option 1:

numbers = set(numbers)
numbers.remove(max(numbers))
max(numbers)

Option 2:

sorted(set(numbers))[-2]
1
Sahil Chhabra

Un moyen simple: 

n=int(input())
arr = set(map(int, input().split()))
arr.remove(max(arr))
print (max(arr))
0
rashedcs

Pour donner une réponse plus générale à la réponse acceptée, voici l’extension permettant d’obtenir la kème valeur la plus grande:

def kth_largest(numbers, k):
    largest_ladder = [float('-inf')] * k
    count = 0
    for x in numbers:
        count += 1
        ladder_pos = 1
        for v in largest_ladder:
            if x > v:
                ladder_pos += 1
            else:
                break
        if ladder_pos > 1:
            largest_ladder = largest_ladder[1:ladder_pos] + [x] + largest_ladder[ladder_pos:]
    return largest_ladder[0] if count >= k else None
0
Bily

Objectif : trouver le deuxième plus grand nombre à partir de l'entrée.

Entrée : 5 2 3 6 6 5

Sortie : 5 

*n = int(raw_input())
arr = map(int, raw_input().split())
print sorted(list(set(arr)))[-2]*
0
Rakesh TS

Vous pouvez aussi essayer ceci:

>>> list=[20, 20, 19, 4, 3, 2, 1,100,200,100]
>>> sorted(set(list), key=int, reverse=True)[1]
100
0

La plupart des réponses précédentes sont correctes mais voici un autre moyen!

Notre stratégie consiste à créer une boucle avec deux variables first_highest et second_highest. Nous parcourons les nombres et si notre valeur current_value est supérieure au premier_highest, nous définissons ensuite second_highest comme étant first_highest et ensuite le second_highest est le nombre actuel. Si notre nombre actuel est supérieur à second_highest, nous définissons ensuite second_highest identique au nombre actuel  enter image description here

#!/usr/bin/env python3
import sys
def find_second_highest(numbers):

    min_integer = -sys.maxsize -1
    first_highest= second_highest = min_integer
    for current_number in numbers:      
        if current_number > first_highest:
            second_highest = first_highest
            first_highest = current_number
        Elif current_number > second_highest:
            second_highest = current_number
    return second_highest

print(find_second_highest([80,90,100]))
0
grepit
n=input("Enter a list:")
n.sort()
l=len(n)
n.remove(n[l-1])
l=len(n)
print n[l-1]
0
Vaisakh MC