在Python

时间:2016-12-10 01:36:13

标签: python

我有以下任务:

  

给定一个未排序的整数数组,找到第一个缺少的正整数。您的算法应该在O(n)时间运行并使用常量空间。

在思考并获得提示之后,我决定更改输入列表A.以下是代码:

def firstMissingPositive(A):
        m=max(A)
        ln=len(A)
        i=0
        while i<ln:
            if A[i]>=1 and A[i]<=ln:
                if A[A[i]-1]!=m+1:

                   A[A[i]-1], A[i] = m+1, A[A[i]-1]
                else:
                    i+=1

            else:
                i+=1
        for i in range(ln):
            if A[i]!=m+1:
                return i+1

当我运行它时,需要很长时间。我该怎么做才能让它快一点?

编辑:这是列表A。

A=[ 417, 929, 845, 462, 675, 175, 73, 867, 14, 201, 777, 407, 80, 882, 785, 563, 209, 261, 776, 362, 730, 74, 649, 465, 353, 801, 503, 154, 998, 286, 520, 692, 68, 805, 835, 210, 819, 341, 564, 215, 984, 643, 381, 793, 726, 213, 866, 706, 97, 538, 308, 797, 883, 59, 328, 743, 694, 607, 729, 821, 32, 672, 130, 13, 76, 724, 384, 444, 884, 192, 917, 75, 551, 96, 418, 840, 235, 433, 290, 954, 549, 950, 21, 711, 781, 132, 296, 44, 439, 164, 401, 505, 923, 136, 317, 548, 787, 224, 23, 185, 6, 350, 822, 457, 489, 133, 31, 830, 386, 671, 999, 255, 222, 944, 952, 637, 523, 494, 916, 95, 734, 908, 90, 541, 470, 941, 876, 264, 880, 761, 535, 738, 128, 772, 39, 553, 656, 603, 868, 292, 117, 966, 259, 619, 836, 818, 493, 592, 380, 500, 599, 839, 268, 67, 591, 126, 773, 635, 800, 842, 536, 668, 896, 260, 664, 506, 280, 435, 618, 398, 533, 647, 373, 713, 745, 478, 129, 844, 640, 886, 972, 62, 636, 79, 600, 263, 52, 719, 665, 376, 351, 623, 276, 66, 316, 813, 663, 831, 160, 237, 567, 928, 543, 508, 638, 487, 234, 997, 307, 480, 620, 890, 216, 147, 271, 989, 872, 994, 488, 291, 331, 8, 769, 481, 924, 166, 89, 824, -4, 590, 416, 17, 814, 728, 18, 673, 662, 410, 727, 667, 631, 660, 625, 683, 33, 436, 930, 91, 141, 948, 138, 113, 253, 56, 432, 744, 302, 211, 262, 968, 945, 396, 240, 594, 684, 958, 343, 879, 155, 395, 288, 550, 482, 557, 826, 598, 795, 914, 892, 690, 964, 981, 150, 179, 515, 205, 265, 823, 799, 190, 236, 24, 498, 229, 420, 753, 936, 191, 366, 935, 434, 311, 920, 167, 817, 220, 219, 741, -2, 674, 330, 909, 162, 443, 412, 974, 294, 864, 971, 760, 225, 681, 689, 608, 931, 427, 687, 466, 894, 303, 390, 242, 339, 252, 20, 218, 499, 232, 184, 490, 4, 957, 597, 477, 354, 677, 691, 25, 580, 897, 542, 186, 359, 346, 409, 655, 979, 853, 411, 344, 358, 559, 765, 383, 484, 181, 82, 514, 582, 593, 77, 228, 921, 348, 453, 274, 449, 106, 657, 783, 782, 811, 333, 305, 784, 581, 746, 858, 249, 479, 652, 270, 429, 614, 903, 102, 378, 575, 119, 196, 12, 990, 356, 277, 169, 70, 518, 282, 676, 137, 622, 616, 357, 913, 161, 3, 589, 327 ]

9 个答案:

答案 0 :(得分:1)

在我看来,在O(n)和恒定空间都这样做是不可能的。(我站得更正, Rockybilly 的链接给出了这样的解决方案)

要在常量空间中执行此操作,有人会强制对列表进行排序,对于大多数排序算法来说,这是O(n log n),这里的那些看起来像插入排序,平均为O(n 2

所以FWIW我选择丢弃恒定空间以便尽可能接近O(n),因为我能想到哪个能给我2n解决方案(在更糟糕的情况下,平均为n + k)(在大O符号中仍然是O(n))

def firstMissingSince(sequence, start=1):
    uniques = set()
    maxitem = start-1
    for e in sequence:
        if e >= start:
            uniques.add(e)
            if e > maxitem:
                maxitem = e
    return next( x for x in range(start, maxitem+2) if x not in uniques )

(版本2轰鸣声)

我本可以使用set(sequence)max(sequence),但两者都是O(n),所以我将它们组合在同一个循环中,我使用set有两个原因:第一个是通过忽略重复可能减少空间,同样我只关心大于或等于我的下限(我也是通用)的数字,其次是O(1)成员资格测试。

最后一行是对缺少元素的简单线性搜索,如果最大元素低于start,则默认为start,如果数组在start和its maximum之间没有缺少元素,则为最大值+ 1。 / p>

这里有一些借鉴其他答案的测试...

assert 1 == firstMissingSince(A) 
assert 2 == firstMissingSince([1,4,3,6,5])
assert 2 == firstMissingSince([1,44,3,66,55]) 
assert 6 == firstMissingSince([1,2,3,4,5]) 
assert 4 == firstMissingSince([-6, 3, 10, 14, 17, 6, 14, 1, -5, -8, 8, 15, 17, -10, 2, 7, 11, 2, 7, 11])
assert 4 == firstMissingSince([18, 2, 13, 3, 3, 0, 14, 1, 18, 12, 6, -1, -3, 15, 11, 13, -8, 7, -8, -7])
assert 4 == firstMissingSince([-6, 3, 10, 14, 17, 6, 14, 1, -5, -8, 8, 15, 17, -10, 2, 7, 11, 2, 7, 11])
assert 3 == firstMissingSince([7, -7, 19, 6, -3, -6, 1, -8, -1, 19, -8, 2, 4, 19, 5, 6, 6, 18, 8, 17])
Rockybilly 的答案让我意识到我根本不需要知道最大值,所以这里是版本2

from itertools import count

def firstMissingSince(sequence, start=1):
    uniques = set(sequence) # { x for x in sequence if x>=start } 
    return next( x for x in count(start) if x not in uniques )

答案 1 :(得分:1)

def first_missing_positive(nums):

    bit = 0

    for n in nums:
        if n > 0:
            bit |= 1 << (n - 1)

    flag = 0

    while bit != 0:
        if (bit & 1 == 0):
            break
        flag += 1
        bit >>= 1

    return flag + 1

O(1)空间复杂度和O(n)时间复杂度解决方案。

答案 2 :(得分:0)

可能不是长时间运行的完整原因,但我确实发现了一个会导致无限循环的错误。我首先创建了长度为20的随机整数数组。

a = [random.randint(-10, 20) for _ in range(20)]

添加了两个打印语句,以查看发生了什么。

    while i<ln:
        print(A)
        if A[i]>=1 and A[i]<=ln:
            if A[A[i]-1]!=m+1:
                print("Swapping %d"%i)
                A[A[i]-1], A[i] = m+1, A[A[i]-1]
            else:
       ...

使用此数组作为输入,您将进入无限循环:

a = [-6, 3, 10, 14, 17, 6, 14, 1, -5, -8, 8, 15, 17, -10, 2, 7, 11, 2, 7, 11]

>>>
...
[18, 18, -8, -10, -6, 6, 14, 18, -5, 18, 18, 15, 17, 18, 2, 7, 18, 18, 7, 11]
Swapping 5
[18, 18, -8, -10, -6, 6, 14, 18, -5, 18, 18, 15, 17, 18, 2, 7, 18, 18, 7, 11]
Swapping 5
[18, 18, -8, -10, -6, 6, 14, 18, -5, 18, 18, 15, 17, 18, 2, 7, 18, 18, 7, 11]
...

事实证明,如果A[A[i]-1]等于A[i],那么您最终会将相同的数字放回A[i]。在这种情况下,i == 5A[5] == 6A[A[i]-1] == 6。在这个声明中,

A[A[i]-1], A[i] = m+1, A[A[i]-1]

评估右侧; m+1被分配到A[5];然后将6分配给A[5]。我通过交换分配顺序修复了这个案例:

A[i], A[A[i]-1] = A[A[i]-1], m+1

使用您添加到问题的列表,它现在会使用我的mod抛出一个IndexError。即使首先对右侧进行评估,看起来左侧的A[A[i]-1]也不会被评估,直到之后进行第一次分配并且大量已经 放置在A[i]

抄袭Rob's solution - 在进行任何互换之前评估[A[i]-1

def firstMissingPositive(A):
    m=max(A)
    ln=len(A)
    print('max:{}, len:{}'.format(m, ln))
    i=0
    while i<ln:
##        print(A[:20])
        if A[i]>=1 and A[i]<=ln:
            if A[A[i]-1]!=m+1:
##                print("Swapping %d"%i)
                v = A[i]-1
                A[i], A[v] = A[v], m+1
            else:
                i+=1
        else:
            i+=1
    for i in range(ln):
        if A[i]!=m+1:
            return i+1

它仍然有时会返回错误的结果,因此减去一个。它会产生以下错误结果:

[18, 2, 13, 3, 3, 0, 14, 1, 18, 12, 6, -1, -3, 15, 11, 13, -8, 7, -8, -7]
[-6, 3, 10, 14, 17, 6, 14, 1, -5, -8, 8, 15, 17, -10, 2, 7, 11, 2, 7, 11]
[7, -7, 19, 6, -3, -6, 1, -8, -1, 19, -8, 2, 4, 19, 5, 6, 6, 18, 8, 17]

答案 3 :(得分:0)

FWIW,我就是这样做的:

def firstMissingPositive(A):
    for i in range(len(A)):
        while A[i] != i+1 and 0 < A[i] < len(A):
            value = A[i]-1
            A[i], A[value] = A[value], A[i]
    for i, value in enumerate(A, 1):
        if i != value:
            return i
    return len(A)+1

assert firstMissingPositive([1,4,3,6,5]) == 2
assert firstMissingPositive([1,44,3,66,55]) == 2
assert firstMissingPositive([1,2,3,4,5]) == 6
assert firstMissingPositive(A) == 1

答案 4 :(得分:0)

      while (input != c && r != c){
          gamesplayed++;
          System.out.println("Games played: " + gamesplayed );
      }

答案 5 :(得分:0)

schedule(dynamic,1)

答案 6 :(得分:0)

下面的我的python代码。 O(n)时间和O(1)空间复杂度。受到this related question中@pmcarpan的高级解决方案的启发。还要在我的github上查看fully commented markdown version

def lowestMissingStrictlyPositiveInteger(arr):
    """ Return the lowest missing strictly 
    positive integer from the array arr. 
    Warning: In order to achieve this in linear time and
    constant space, arr is modified in-place.
    
    Uses separatePositiveIntegers() to isolate all
    strictly positive integers, and marks their occurrence
    with markIndicesOfObservedPositiveIntegers(). This 
    function then scans the modified array for the 'marks'
    and returns the first unmarked value. """
    m = separatePositiveIntegers(arr)
    markIndicesOfObservedPositiveIntegers(arr, m)
    for i in range(m): #O(m)
        if arr[i]>0:
            # this index hasn't been marked by
            # markIndexOfObservedPositiveIntegers(), 
            # therefore the integer i+1 is missing.
            return i+1
    return m+1

def separatePositiveIntegers(arr):
    """ Modify input array in place, so that 
    strictly positive integers are
    all at the start of the array, 
    and negative integers are
    all at the end of the array. 
    
    Return the index of the first negative 
    integer in the updated array (or len(arr)
    if all values are positive). """
    i1, i2 = 0, len(arr)-1
    while (i2 > i1): #O(n)
        
        if arr[i2]<=0:
            # move to the first strictly positive value
            # starting from the end of the array.
            i2 -= 1
            continue
        
        if arr[i1]>0:
            # move to the first negative value
            # from the start of the array.
            i1 += 1
            continue
        
        # swap negative value at i1 with the first
        # strictly positive value starting from the
        # end of the array (i.e., at position i2).
        tmp = arr[i2]
        arr[i2] = arr[i1]
        arr[i1] = tmp
    
    return i1 if arr[i1]<=0 else i1+1

def markIndicesOfObservedPositiveIntegers(arr, m):
    """ Take an array arr of integer values, 
    where indices [0,m-1] are all strictly positive
    and indices >= m are all negative
    (see separatePositiveIntegers() method).
    
    Mark the occurrence of a strictly positive integer
    k<=m by assigning a negative sign to the value in 
    the array at index k-1 (modify in place)."""
    for i in range(m): #O(m)
        # all values at indices [0,m-1] are strictly positive 
        # to start with, but may have been  modified in-place 
        # (switched to negative sign) in this loop. 
        # Therefore, read the untampered value as abs(arr[i]).
        untampered_val=abs(arr[i])
        # We can safely ignore any untampered value strictly superior to m
        # because it guarantees a gap in the integer sequence at a lower value 
        # (since arr only has m strictly positive integers).
        if untampered_val<=m:
            # mark the integer as "seen" by
            # changing the sign of the value at
            # index untampered_val-1 to negative.
            arr[untampered_val-1] = -abs(arr[untampered_val-1])

# test 1
arr = [3, 4, -1, 1]
assert lowestMissingStrictlyPositiveInteger(arr) == 2

# test 2
arr = [2, 0, 1]
assert lowestMissingStrictlyPositiveInteger(arr) == 3

# test 3
arr = [0]
assert lowestMissingStrictlyPositiveInteger(arr) == 1

答案 7 :(得分:-1)

这是我的解决方案。

from collections import defaultdict

def firstMissingPositive(A):

    d = defaultdict(int)
    for i in A:
        d[i] = 1

    j = 1
    while True:
        if d[j] == 0:
            return j
        j += 1

空间复杂性: O(n)

时间复杂度: O(n + k)。这被认为是O(n)。也忽略了散列复杂性。

顺便说一下: Googling给出了你寻求的答案,恒定的空间和O(n)时间。

答案 8 :(得分:-1)

  • 复杂度:O(N)或O(N * log(N))
  • 如果您要进行亲切测试,您将获得100%的分数
  • 背后的想法是使用1个循环和一次字典来避免O(N ^ 2)复杂性
  • 问题中提到的列表花费的时间:0:00:00.000098
def test(A):
    value = 1
    data = {}
    for num in A:
        if num < 0:
            continue
        elif num > 0 and num != value:
            data[num] = 1
            continue
        elif num == value:
            data[num] = 1
            while data.get(value, None) is not None:
                value += 1
    return value