比较两个列表并仅打印出它们的不同之处?(对两个列表执行异或操作)

14

我尝试创建一个函数,该函数接受两个列表并返回仅包含这两个列表之间的差异的列表。

示例:

a = [1,2,5,7,9]
b = [1,2,4,8,9]

输出结果应为[4,5,7,8]

函数目前的代码:

def xor(list1, list2):
    list3=list1+list2
    for i in range(0, len(list3)):
        x=list3[i]
        y=i
        while y>0 and x<list3[y-1]:
            list3[y]=list3[y-1]
            y=y-1
        list3[y]=x

        last=list3[-1]
    for i in range(len(list3) -2, -1, -1):
        if last==list3[i]:
            del list3[i]
        else:
            last=list3[i]

    return list3 
print xor([1,2,5,7,8],[1,2,4,8,9])
第一个for循环对其进行排序,第二个for循环删除重复项。问题是结果为[1,2,4,5,7,8,9]而不是[4,5,7,8],所以它并没有完全删除重复项?我应该添加什么来完成这个任务。基本上只能使用循环,不能使用任何特殊模块、.sort、set或其他东西。
6个回答

20

使用 Set 更好。

>>> a = [1,2,5,7,9]
>>> b = [1,2,4,8,9]
>>> set(a).symmetric_difference(b)
{4, 5, 7, 8}
感谢@DSM,他提供了更好的句子:
>>> set(a)^set(b)

这两个语句是相同的,但后者更加清晰。

更新:抱歉,我没有看到最后的要求:不能使用set。就我所看到的,@sashkello提供的解决方案是最好的。


1
非常好的回答,但是“我不能使用任何特殊模块、.sort、set或其他任何东西,基本上只能使用循环。” - Patashu
3
如果我们使用集合,为什么不直接使用 set(a) ^ set(b) 呢? - DSM
@DSM 谢谢,你的建议更好。我只是忘记了语法。 - Sheng
我其实和“^”一样喜欢symmetric_difference,但我更喜欢你原来的长版本。但是当我评论时,你同时把它改成了symmetric_difference。 :^) - DSM
@Sheng的更新,我们可以通过简单地使用list(set(a)^set(b))将其转换为列表#返回一个列表 - usct01

18

如果您想将一个元素添加到新列表中,前提是它在其中一个列表中存在而在另一个列表中不存在,可以使用以下简洁的循环。对于两个列表中的每个元素(使用 list1+list2 连接它们),如果该元素不存在于其中一个列表中,则将其添加:

[a for a in list1+list2 if (a not in list1) or (a not in list2)]
你现在的代码通过明确地循环遍历元素可以轻松转化为更不符合Python的代码,但老实说我没看出有什么意义(尽管这并不重要):

你现在的代码通过明确地循环遍历元素可以轻松转化为更不符合 Python 的代码,但老实说我没看出有什么意义(尽管这并不重要):

def xor(list1, list2):
    outputlist = []
    list3 = list1 + list2
    for i in range(0, len(list3)):
        if ((list3[i] not in list1) or (list3[i] not in list2)) and (list3[i] not in outputlist):
             outputlist[len(outputlist):] = [list3[i]]
    return outputlist

我认为我理解它在做什么,但是我如何将其制作成完整的循环?我应该将此循环添加到原始函数中还是它可以独立工作? - user2314520
这是一个一行代码的函数... 你需要循环遍历list1和list2中的元素x,并有两个if条件:1. 如果元素在list1中,则将flag1设置为True,2. 如果元素在list2中,则将flag2设置为True;如果(flag1和flag2)不等于True且x不在outputlist中,则将其添加到outputlist中。 - sashkello
1
@user2314520:另一种看待它的方式是:它找到两个列表的并集,但不包括交集(即,它找到了对称差异)。并集是list1 + list2。如果一个元素x在交集中,那么x既在list1中又在list2中;你想要排除这样的元素,所以条件应该被反转:not (x in list1 and x in list2),这等价于x not in list1 or x not in list2。把所有东西放在一起,xor_list = [x for x in (list1 + list2) if x not in list1 or x not in list2] - jfs
@sashkello:这里是您多行代码的稍微更易读的版本(注意:它会保留仅存在于一个列表中的重复项(以及使用列表推导式的代码))。 - jfs
@J.F.Sebastian 谢谢,是的,我假设我们没有(也许不想要)重复。 - sashkello
显示剩余2条评论

5
注意:这种方法并不符合Python的编程风格,仅应作为课堂作业答案使用 :)
在你对两个列表进行排序后,可以通过以下方式找到重复元素:
1)将迭代器放置在A和B的开始位置
2)如果Aitr大于Bitr,则在将Bitr的值放入返回列表后将Bitr向前推进
3)否则,如果Bitr大于Aitr,则在将Aitr的值放入返回列表后将Aiter向前推进
4)否则,你找到了一个重复元素,将Aitr和Bitr都向前推进

你为什么认为你的方法不符合Pythonic风格? - John La Rooy
@gnibbler 请参考Sheng的答案,这是我认为符合Python风格的。 - Patashu
这取决于所有元素都是可哈希的。(它们很可能是) - John La Rooy
这是一个关于算法的问题,涉及到两种实现方式。具体内容请参见链接:https://dev59.com/TGQo5IYBdhLWcg3wG8SE#WZ4IoYgBc1ULPQZFLQcm。 - jfs

3
这段代码的前提是你已经有了排序好的列表。它可以在线性时间内运行,而不像其他解决方案一样需要二次方时间复杂度。
def diff(sl0, sl1):
    i0, i1 = 0, 0
    while i0 < len(sl0) and i1 < len(sl1):
        if sl0[i0] == sl1[i1]:
            i0 += 1
            i1 += 1
        elif sl0[i0] < sl1[i1]:
            yield sl0[i0]
            i0 += 1
        else:
            yield sl1[i1]
            i1 += 1
    for i in xrange(i0, len(sl0)):
        yield sl0[i]
    for i in xrange(i1, len(sl1)):
        yield sl1[i]

print list(diff([1,2,5,7,9], [1,2,4,8,9]))

线性解决方案加1。通常,您可以将其适应于接受任何可迭代对象,本质上实现了@Patashu的算法(https://dev59.com/TGQo5IYBdhLWcg3wG8SE#16312759)。 - jfs

3

尝试这个:

    a = [1,2,5,7,9]
    b = [1,2,4,8,9]
 print set(a).symmetric_difference(set(b))

1
简单,但不是特别有效 :)
>>> a = [1,2,5,7,9]
>>> b = [1,2,4,8,9]
>>> [i for i in a+b if (a+b).count(i)==1]
[5, 7, 4, 8]

或者使用“仅循环”。
>>> res = []
>>> for i in a+b:
...  c = 0
...  for j in a+b:
...   if i==j:
...    c += 1
...  if c == 1:
...   res.append(i)
... 
>>> res
[5, 7, 4, 8]

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