以线性时间获得列表中的第二大数字

时间:2013-04-25 22:16:23

标签: python performance

我正在学习Python,并且处理列表的简单方法是一种优势。有时它是,但看看这个:

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

从列表中获取第二大数字的一种非常简单快捷的方法。除了简单列表处理有助于编写两次遍历列表的程序,找到最大的然后是第二大的。它也具有破坏性 - 如果我想保留原始数据,我需要两份数据。我们需要:

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

它只运行一次列表,但不像以前的解决方案那样简洁明了。

所以:在这种情况下,有两种方法可以同时使用吗?第一个版本的清晰度,但是单个版本的第二个版本?

28 个答案:

答案 0 :(得分:47)

您可以使用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]

从那里开始......

答案 1 :(得分:23)

由于@OscarLopez和我对第二大意味着什么有不同意见,我会根据我的愿景发布代码,并与提问者提供的第一个算法一致。

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

(注意:此处使用负无穷大而不是None,因为None在Python 2和3中具有不同的排序行为 - 请参阅Python - Find second smallest number;检查{{3}}中的元素数量{1}}确保在未定义实际答案时不会返回负无穷大。)

如果最大值出现多次,那么它也可能是第二大的。关于这种方法的另一个问题是,如果少于两个元素,它可以正常工作;那时没有第二大。

运行相同的测试:

numbers

<强>更新

我重组了条件以大幅提高性能;我的随机数测试几乎达到了100%。原因是在原始版本中,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 始终在下一个数字不是列表中最大的情况下进行评估。换句话说,对于列表中的几乎每个数字,进行了两次比较,而一次比较大部分就足够了 - 如果数字不大于第二大数字,则它也不大于最大值。

答案 2 :(得分:17)

您可以随时使用sorted

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

答案 3 :(得分:11)

尝试下面的解决方案,它是O(n),它将存储并返回second变量中第二大的数字。请注意,如果numbers中的所有元素都相同,或者numbers为空或者包含单个元素,则变量second的最终值为None - 这是正确的,因为在那些情况下没有“第二大”元素。

小心:这会找到“第二个最大值”,如果有多个值是“第一个最大值”,它们将被视为相同的最大值 - 在我的定义中,在这样的列表:[10, 7, 10]正确的答案是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

以下是一些测试:

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

答案 4 :(得分:3)

快速排行的quickselect algorithm,O(n)堂兄会做你想做的事。 Quickselect的平均性能为O(n)。最糟糕的情况是O(n ^ 2),就像快速排序一样,但很少见,而对quickselect的修改会将最坏情况的性能降低到O(n)。

快速选择的想法是使用相同的枢轴,更低,更高的快速排序的想法,但然后忽略下部并进一步订购更高的部分。

答案 5 :(得分:3)

>>> l = [19, 1, 2, 3, 4, 20, 20]
>>> sorted(set(l))[-2]
19

答案 6 :(得分:3)

为什么要使场景复杂化?它非常简单直接

  1. 将列表转换为设置 - 删除重复项
  2. 再次将设置转换为列表 - 按升序显示列表
  3. 这是一个代码

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

答案 7 :(得分:2)

这里有类型([])的一些好答案,如果有人在类型({})上需要相同的东西,这里就是,

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)])

为了好玩,我试图让用户友好的xD

答案 8 :(得分:2)

您可以通过以下任意一种方式找到第二大的东西:

选项1:

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

选项2:

sorted(set(numbers))[-2]

答案 9 :(得分:2)

如果你不介意使用numpy(import numpy as np):

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

给你the 2nd largest element of the list with a guaranteed worst-case O(n) running time

partition(a, kth)方法返回一个数组,其中k元素与排序数组中的元素相同,之前的所有元素都较小,后面的所有元素都较大。

答案 10 :(得分:1)

def secondlarget(passinput):
    passinputMax = max(passinput)  #find the maximum element from the array
    newpassinput = [i for i in passinput if i != passinputMax]  #Find the second largest element in the array
    #print (newpassinput)
    if len(newpassinput) > 0:
        return max(newpassinput) #return the second largest
    return 0
if __name__ == '__main__':
    n = int(input().strip())  # lets say 5
    passinput = list(map(int, input().rstrip().split())) # 1 2 2 3 3
    result = secondlarget(passinput) #2
    print (result) #2

答案 11 :(得分:1)

以前的大多数答案都是正确的,但这是另一种方法!

我们的策略是创建一个包含两个变量first_highest和second_highest的循环。我们遍历数字,如果我们的current_value大于first_highest,则将second_highest设置为与first_highest相同,然后将second_highest设置为当前数字。如果我们当前的数字大于second_highest,那么我们将second_highest设置为与当前数字相同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]))

答案 12 :(得分:1)

    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])

答案 13 :(得分:1)

这可以在[N + log(N)-2]时间内完成,这稍微好于2N的松散上限(也可以认为是O(N))。

诀窍是使用二进制递归调用和“网球锦标赛”算法。所有'比赛'(N-1次)之后将出现获胜者(最大数量),但如果我们记录所有比赛的'球员',并且在其中,将获胜者击败的所有球员分组,第二大数字将是该组中最大的数字,即“输家”组。

这个'输家'组的大小是log(N),同样,我们可以撤销二进制递归调用以找到输入中最大的,这将花费[log(N) - 1]时间。实际上,我们可以直接扫描输家组以获得答案,时间预算是相同的。

下面是一个示例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 __name__ == "__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))

答案 14 :(得分:1)

O(n):如果循环变量递增/递减恒定量,则循环的时间复杂度被视为O(n)。例如,以下函数具有O(n)时间复杂度。

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

要查找第二大数字,我使用以下方法首先找到最大数字,然后搜索列表中是否存在

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

答案 15 :(得分:0)

我的朋友Dhanush Kumar提出的最佳解决方案:

    def second_max(loop):
        glo_max = loop[0]
        sec_max = float("-inf")
        for i in loop:
            if i > glo_max:
                sec_max = glo_max
                glo_max=i
            elif sec_max < i < glo_max:
                sec_max = i
        return sec_max
    
    #print(second_max([-1,-3,-4,-5,-7]))
    
    assert second_max([-1,-3,-4,-5,-7])==-3
    assert second_max([5,3,5,1,2]) == 3
    assert second_max([1,2,3,4,5,7]) ==5
    assert second_max([-3,1,2,5,-2,3,4]) == 4
    assert second_max([-3,-2,5,-1,0]) == 0
    assert second_max([0,0,0,1,0]) == 0

答案 16 :(得分:0)

list_nums = [1, 2, 6, 6, 5]
minimum = float('-inf')
max, min = minimum, minimum
for num in list_nums:
    if num > max:
        max, min = num, max
    elif max > num > min:
        min = num
print(min if min != minimum else None)

输出

5

答案 17 :(得分:0)

您必须在新值之间进行比较,这才是窍门,始终认为前一个(第二大)应该在最大值和前一个最大值之间,即那个!!

def secondLargest(lista):
    max_number   = 0
    prev_number  = 0

    for i in range(0, len(lista)):

        if lista[i] > max_number:
            prev_number = max_number
            max_number  = lista[i]
        elif lista[i] > prev_number and lista[i] < max_number:
            prev_number = lista[i]

    return prev_number

答案 18 :(得分:0)

下面的代码将在不使用 max 函数的情况下找到最大和第二个最大数字。我假设输入是数字,数字之间用一个空格分隔。

myList = input().split()
myList = list(map(eval,myList))


m1 = myList[0]
m2 = myList[0]

for x in myList:
    if x > m1:
        m2 = m1
        m1 = x
    elif x > m2:
        m2 = x

print ('Max Number: ',m1)
print ('2nd Max Number: ',m2)

答案 19 :(得分:0)

if __name__ == '__main__':
    n = int(input())
    arr = list(map(float, input().split()))
    high = max(arr)
    secondhigh = min(arr)
    for x in arr:
        if x < high and x > secondhigh:
            secondhigh = x
    print(secondhigh)

上面的代码是当我们在列表中设置元素值时 根据用户要求。下面的代码是根据所问的问题

#list
numbers = [20, 67, 3 ,2.6, 7, 74, 2.8, 90.8, 52.8, 4, 3, 2, 5, 7]
#find the highest element in the list
high = max(numbers)
#find the lowest element in the list
secondhigh = min(numbers)
for x in numbers:
    '''
    find the second highest element in the list,
    it works even when there are duplicates highest element in the list.
    It runs through the entire list finding the next lowest element
    which is less then highest element but greater than lowest element in
    the list set initially. And assign that value to secondhigh variable, so 
    now this variable will have next lowest element in the list. And by end 
    of loop it will have the second highest element in the list  
    '''
    if (x<high and x>secondhigh):
        secondhigh=x
print(secondhigh)

答案 20 :(得分:0)

在这里我试图想出一个答案。 使用单循环且不使用任何内置函数的列表中的第二(第二)个最大元素。

def secondLargest(lst):
    mx = 0
    num = 0
    sec = 0
    for i in lst:
        if i > mx:
            sec = mx
            mx = i
        else:
            if i > num and num >= sec:
                sec = i
            num = i
    return sec

答案 21 :(得分:0)

只是为了使接受的答案更笼统,以下是获得第k个最大值的扩展:

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

答案 22 :(得分:0)

一种简单的方法:

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

答案 23 :(得分:0)

目标:从输入中找到第二大数字。

输入:5         2 3 6 6 5

输出:5

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

答案 24 :(得分:0)

你也可以试试这个:

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

答案 25 :(得分:0)

function handleFileSelect(evt) {
    evt.stopPropagation();
    evt.preventDefault();

    var files = evt.dataTransfer.files; // FileList object.

    // files is a FileList of File objects. List some properties.
    var output = [];
    for (var i = 0, f; f = files[i]; i++) {
        output.push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
        f.size, ' bytes, last modified: ',
        f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a',
            '</li>');
    }
    $('#list').innerHTML = '<ul>' + output.join('') + '</ul>';
}

function handleDragOver(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
}

// This is necessary or jQuery won't include the data property when an event is triggered
// It's recommended you use the native event listeners, as this method will add extra overhead.
$.event.props.push("dataTransfer");

// Setup the dnd listeners.
window.onload = function(e) {
    var dropZone = $('#drop_zone');
    dropZone.on('dragover', handleDragOver);
    dropZone.on('drop', handleFileSelect);
}

答案 26 :(得分:-1)

通过将每个值与max_item进行比较来最大化该值。在第一个if中,每次max_item的值更改时,它都会将其先前的值赋予second_max。如果确定边界,则紧紧耦合两秒钟

def secondmax(self, list):
    max_item = list[0]
    second_max = list[1]
    for item in list:
        if item > max_item:
            second_max =  max_item
            max_item = item
        if max_item < second_max:
            max_item = second_max
    return second_max

答案 27 :(得分:-1)

使用defalut sort()方法获取列表中的第二大数字。 sort是在构建方法中,您不需要为此导入模块。

lis = [11,52,63,85,14]
lis.sort()
print(lis[len(lis)-2])