И б. Я хочу удалить все числа в списке b, где есть '0', а также соответствующие числа в a, которые имеют тот же индекс с нулями в b. Это мой код:

a = [ 1 , 23 , 3 , 45 , 5 , 63 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15]

b = [ 8 , 0  , 0 , 7  , 0 , 9  , 3 , 2 , 4 , 13 , 25 , 45 , 34 , 25 , 11]


indexzeroes = [i for i, j in enumerate(b) if j == 0]


for i in indexzeroes:
    b.pop(i)
    a.pop(i)

print a
print b

Однако я получаю неправильные обновленные списки для a и b. Я определил причину, заключающуюся в том, что в цикле «for» я менял структуру списка каждый раз, когда «выталкивал» элемент, так что меняются и индексы с остальными нулями.

Это кажется настолько запутанным для такой якобы простой проблемы. Кто-нибудь может помочь?

3
piccolo 14 Дек 2015 в 19:37

3 ответа

Лучший ответ

Причина в том, что когда вы pop i -й индекс из a или b, все элементы сдвигаются влево один раз. Вы можете решить это, вставив элементы в обратном порядке (поскольку индексы должны быть в отсортированном порядке):

for i in reversed(indexzeroes):
    a.pop(i)
    b.pop(i)

С учетом сказанного, это, вероятно, несколько неэффективно для больших списков (наихудший случай O (n ^ 2)). Вам лучше использовать set, который даст вам алгоритм O(n) за счет небольшой дополнительной памяти:

indexzeroes = {i for i, j in enumerate(b) if j == 0}
a = [x for i, x in enumerate(a) if i not in indexzeros]
b = [x for i, x in enumerate(b) if i not in indexzeros]
5
mgilson 14 Дек 2015 в 16:39

В качестве альтернативного подхода, вы могли бы использовать понимание списка следующим образом:

a = [1, 23, 3, 45, 5, 63, 7, 8, 9, 10, 11, 12, 13, 14, 15]
b = [8, 0 , 0, 7 , 0, 9 , 3, 2, 4, 13, 25, 45, 34, 25, 11]

a_out = []
b_out = []

[(a_out.append(v1), b_out.append(v2)) for v1, v2 in zip(a,b) if v2]

print a_out
print b_out

Это будет отображать следующее:

[1, 45, 63, 7, 8, 9, 10, 11, 12, 13, 14, 15]
[8, 7, 9, 3, 2, 4, 13, 25, 45, 34, 25, 11]

Если списки велики, вы также можете переключиться на использование itertools.izip вместо zip.

Или как предложено, используя ваниль for-loop:

a = [1, 23, 3, 45, 5, 63, 7, 8, 9, 10, 11, 12, 13, 14, 15]
b = [8, 0 , 0, 7 , 0, 9 , 3, 2, 4, 13, 25, 45, 34, 25, 11]

a_out = []
b_out = []

for v1, v2 in zip(a,b):
    if v2:
        a_out.append(v1)
        b_out.append(v2)

print a_out
print b_out
1
Martin Evans 14 Дек 2015 в 20:12

Вы можете удалить, если вы начинаете с конца списков:

for ind in range(len(a) - 1, -1, -1):
    if b[ind] == 0:
        del a[ind]
        del b[ind]

Выход:

[1, 45, 63, 7, 8, 9, 10, 11, 12, 13, 14, 15]
[8, 7, 9, 3, 2, 4, 13, 25, 45, 34, 25, 11]

Начиная с конца работает, так как списки становятся меньше, поэтому любой элемент, который мы еще не видели, будет по-прежнему с тем же индексом, что и индекс меньше, чем все, что мы видели до сих пор. Вы также должны действительно использовать list.pop, только если вы хотите использовать возвращаемый элемент, если вы просто хотите удалить, то list.remove или del, как указано выше, будет лучшим.

Вы также можете сделать это за O(n) время с помощью набора, создав новые списки:

a = [1, 23, 3, 45, 5, 63, 7, 8, 9, 10, 11, 12, 13, 14, 15]

b = [8, 0, 0, 7, 0, 9, 3, 2, 4, 13, 25, 45, 34, 25, 11]
from itertools import izip

def remove_(i,l1,l2):
    c,d = [], []
    for j, k in izip(l1, l2):
        if k != i:
            c.append(j),d.append(k)
    return c, d

a, b= remove_(0, a, b)

print(a,b)
1
Padraic Cunningham 14 Дек 2015 в 17:36