如何在循环中更新数组索引(IndexError:索引超出列表范围)

4

我不能使用高级函数,因为这是面试中的逻辑测试。尝试删除数组中出现多次的所有数字。

测试用例: a = [1, 1, 2, 3, 2, 4, 5, 6, 7]

代码:

    def dup(a):
      i=0
      arraySize = len(a)
      print(arraySize)
        while i < arraySize:
        #print("1 = ",arraySize)
        k=i+1
          for k in range(k,arraySize):
          if a[i] == a[k]:
          a.remove(a[k])
        arraySize -= 1
        #print("2 = ",arraySize)
      i += 1
    print(a) 

结果应该是:1,2,3,4,5,6,7。
但是我一直收到索引超出范围的错误。我知道这是因为循环内部的数组列表发生了变化,所以“while”循环的初始索引与新索引不同。
问题是:是否有办法将新索引长度(循环内的数组)与父循环(“while”循环中的索引)同步?
我能想到的唯一办法是在循环内使用函数。
有任何提示吗?

2
你正在走上正轨,因为循环内部的数组列表已经改变。由于在迭代时修改了列表,然后到达不存在的索引,导致了此错误。对于你当前的实现,应该使用全新的列表,当你发现一个值不存在于列表中时,只需将其附加到列表即可。顺便提一下,你的解决方案可以简写为 list(set(a)) - idjaw
在Python中迭代时不要更改正在迭代的对象,否则迭代器将不会被更新。 - pstatix
为了更好地解释,这里有一个问题涉及到修改正在迭代的相同列表在这里 - idjaw
2个回答

3

重新计算每次迭代的数组大小

看起来我们有几个问题。首先,您无法在内部循环(range函数)中更新“stop”值。因此,首先让我们删除它,并使用另一个while循环,使我们能够在每次迭代中重新计算数组大小。

重新检查移除列表位置的值

接下来,在您解决这个问题后,您将遇到一个更大的问题。当您使用remove时,它会将值从列表末尾移动或将整个列表向左移动以使用已删除的位置,而您没有重新检查移动到旧值已被删除的位置的值。为了解决这个问题,我们需要在删除元素时递减i,这确保我们正在检查放置到已删除元素位置的值。

removedel

在这种情况下,您应该使用del而不是removeremove遍历列表并删除第一次出现的值,而我们似乎已经知道要删除的值的确切索引。 remove可能有效,但在这里使用它会使事情变得过于复杂。

最小更改功能代码

def dup(a):
    i = 0
    arraySize = len(a)
    print(arraySize)
    while i < arraySize:
        k = i + 1
        while k < arraySize: # CHANGE: use a while loop to have greater control over the array size.
            if a[i] == a[k]:
                print("Duplicate found at indexes %d and %d." % (i, k))
                del a[i] # CHANGE: used del instead of remove.
                i -= 1 # CHANGE: you need to recheck the new value that got placed into the old removed spot.
                arraySize -= 1
                break
            k += 1
        i += 1
    return a

现在,我想指出我们上面的代码存在一些可读性和可维护性问题。按照我们目前的做法迭代数组并对迭代器进行操作有点凌乱,而且可能容易出错。下面是我会以更可读性和可维护性的方式实现这个问题的几种方法。

简单易懂的替代方案

def remove_duplicates(old_numbers):
    """ Simple/naive implementation to remove duplicate numbers from a list of numbers. """
    new_numbers = []
    for old_number in old_numbers:
        is_duplicate = False
        for new_number in new_numbers:
            if old_number == new_number:
                is_duplicate = True
        if is_duplicate == False:
            new_numbers.append(old_number)
    return new_numbers

优化的低级别替代方案

def remove_duplicates(numbers):
""" Removes all duplicates in the list of numbers in place. """
    for i in range(len(numbers) - 1, -1, -1):
        for k in range(i, -1, -1):
            if i != k and numbers[i] == numbers[k]:
                print("Duplicate found. Removing number at index: %d" % i)
                del numbers[i]
                break
    return numbers

哇,它没有索引错误,是因为反向循环吗? - system programmer
啊,我现在明白了,因为 "i" 变量与 "number[i]" 一起增加(或减少)。聪明!谢谢。 - system programmer
我认为你已经明白了,通过反向迭代,我们避免了每次迭代需要重新计算数组大小的问题。我们也避免了我回答中第一句话描述的“移位”问题。 - Brennen Sprimont

0
你可以将另一个列表中的内容复制并从中删除重复项,然后返回该列表。例如:
duplicate = a.copy()  

f = 0  
for j in range(len(a)):  
    for i in range(len(duplicate)):  
        if i < len(duplicate):  
            if a[j] == duplicate[i]:  
                f = f+1  
                if f > 1:  
                    f = 0  
                    duplicate.remove(duplicate[i])  
    f=0  
print(duplicate) 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接