Python中列表中的重复元素

8

假设我有一个字符串列表:

a = ['a', 'a', 'b', 'c', 'c', 'c', 'd']

我想列出在列表中至少连续出现两次的项目:
result = ['a', 'c']

我知道我必须使用for循环,但我想不出如何针对一行中重复的项目。 我该怎么做?
编辑:如果同一项在a中重复两次怎么办?那么set函数就无效了。
a = ['a', 'b', 'a', 'a', 'c', 'a', 'a', 'a', 'd', 'd']
result = ['a', 'a', 'd']
9个回答

7

在这里尝试使用itertools.groupby()

>>> from itertools import groupby,islice
>>> a = ['a', 'a', 'b', 'c', 'c', 'c', 'b']

>>> [list(g) for k,g in groupby(a)]
[['a', 'a'], ['b'], ['c', 'c', 'c'], ['b']] 

>>> [k for k,g in groupby(a) if len(list(g))>=2]
['a', 'c']

使用 islice()

>>> [k for k,g in groupby(a) if len(list(islice(g,0,2)))==2]
>>> ['a', 'c']

使用 zip()izip()

In [198]: set(x[0] for x in izip(a,a[1:]) if x[0]==x[1])
Out[198]: set(['a', 'c'])

In [199]: set(x[0] for x in zip(a,a[1:]) if x[0]==x[1])
Out[199]: set(['a', 'c'])

timeit 的结果:

from itertools import *

a='aaaabbbccccddddefgggghhhhhiiiiiijjjkkklllmnooooooppppppppqqqqqqsssstuuvv'

def grp_isl():
    [k for k,g in groupby(a) if len(list(islice(g,0,2)))==2]

def grpby():
    [k for k,g in groupby(a) if len(list(g))>=2]

def chn():
    set(x[1] for x in chain(izip(*([iter(a)] * 2)), izip(*([iter(a[1:])] * 2))) if x[0] == x[1])

def dread():
    set(a[i] for i in range(1, len(a)) if a[i] == a[i-1])

def xdread():
    set(a[i] for i in xrange(1, len(a)) if a[i] == a[i-1])

def inrow():
    inRow = []
    last = None
    for x in a:
        if last == x and (len(inRow) == 0 or inRow[-1] != x):
            inRow.append(last)
        last = x

def zipp():
    set(x[0] for x in zip(a,a[1:]) if x[0]==x[1])

def izipp():
    set(x[0] for x in izip(a,a[1:]) if x[0]==x[1])

if __name__=="__main__":
    import timeit
    print "islice",timeit.timeit("grp_isl()", setup="from __main__ import grp_isl")
    print "grpby",timeit.timeit("grpby()", setup="from __main__ import grpby")
    print "dread",timeit.timeit("dread()", setup="from __main__ import dread")
    print "xdread",timeit.timeit("xdread()", setup="from __main__ import xdread")
    print "chain",timeit.timeit("chn()", setup="from __main__ import chn")
    print "inrow",timeit.timeit("inrow()", setup="from __main__ import inrow")
    print "zip",timeit.timeit("zipp()", setup="from __main__ import zipp")
    print "izip",timeit.timeit("izipp()", setup="from __main__ import izipp")

输出:

islice 39.9123107277
grpby 30.1204478987
dread 17.8041124706
xdread 15.3691785568
chain 17.4777339702
inrow 11.8577565327           
zip 16.6348844045
izip 15.1468557105
结论: 根据比较,Poke的解决方案是最快的选择。

1
我觉得你错过了“连续”这一部分。除此之外是一个不错的简单解决方案,即使它是O(n^2)的。 - Mark Ransom
计数器仍然没有“连续”的功能。 - g.d.d.c
@g.d.d.c 什么是“in a row”?我不明白。 :| - Ashwini Chaudhary
该元素一遍又一遍地重复出现,因此 f(a a a b c c c c b) 只会返回 a c - Blender
@AshwiniChaudhary 感谢您详细的回答!如果 a = ['r', 'g', 'r', 'r', 'p', 'r', 'r', 'r'],那么 f(a) 将返回 [r, r]。set 函数将抑制重复元素的后一行。 - Albert
显示剩余7条评论

5
这听起来像是一道作业题,因此我将大纲概述一下我会做的事情:
  1. 遍历a,但保留每个元素的索引变量。使用enumerate()很有用。
  2. for循环中,从当前项目的索引开始一个while循环。
  3. 只要下一个元素与前一个元素(或原始元素)相同,就重复循环。这里可以使用break
  4. 计算该循环重复的次数(您需要为此计数器变量)。
  5. 如果计数器变量>=2,则将该项附加到result中。

请注意,本文中包含HTML标记,请不要删除它们。

对于看起来像作业的问题,提供方法而非代码的回答会得到加分。 - Silas Ray

3
我的看法:
>>> a = ['a', 'a', 'b', 'c', 'c', 'c', 'd']
>>> inRow = []
>>> last = None
>>> for x in a:
        if last == x and (len(inRow) == 0 or inRow[-1] != x):
            inRow.append(last)
        last = x
>>> inRow
['a', 'c']

3
如何使用以下代码实现:将列表中相邻的重复元素放入一个集合中?

set([a[i] for i in range(1, len(a)) if a[i] == a[i-1]])


1
这是最优雅的解决方案。我会在这里使用xrange而不是range,这样你可以节省一些时间和大量内存。 - kreativitea

2

这里有一个Python一行代码,可以完成您想要的功能。它使用了 itertools 包:

from itertools import chain, izip

a = "aabbbdeefggh" 

set(x[1] for x in chain(izip(*([iter(a)] * 2)), izip(*([iter(a[1:])] * 2))) if x[0] == x[1])

这个程序在输入a="aabbbdeefggh"时给出了错误的输出,我期望得到的是{'a', 'b', 'e', 'g'},但实际上只得到了{'a', 'b', 'e'} - Ashwini Chaudhary
我能理解为什么会发生这种情况。它只创建“偶数”对。这可以通过使用奇数对重复该过程来解决。 - Will
现在已经更新以解决那个问题。还有一个特殊情况,但只需要一行代码就能解决 :-) - Will
我更新了它以摆脱它。当列表是奇数并且迭代器“包裹”到第二次通过时,它会将“偶数”列表的最后一个元素与“奇数”列表的第一个元素配对。 - Will
+1 我刚刚计时了你的解决方案,它比groupby()快得多,我在我的解决方案中发布了timeit结果。 - Ashwini Chaudhary

1

编辑后的问题要求避免使用set(),排除了大部分答案。

我想比较一下@poke的好老循环和我创建的另一个花哨的一行列表理解:

from itertools import *

a = 'aaaabbbccccaaaaefgggghhhhhiiiiiijjjkkklllmnooooooaaaaaaaaqqqqqqsssstuuvv'

def izipp():
    return set(x[0] for x in izip(a, a[1:]) if x[0] == x[1])

def grpby():
    return [k for k,g in groupby(a) if len(list(g))>=2]

def poke():
    inRow = []
    last = None
    for x in a:
        if last == x and (len(inRow) == 0 or inRow[-1] != x):
            inRow.append(last)
        last = x
    return inRow    

def dread2():
    repeated_chars = []
    previous_char = ''
    for char in a:
        if repeated_chars and char == repeated_chars[-1]:
            continue
        if char == previous_char:
            repeated_chars.append(char)
        else:
            previous_char = char
    return repeated_chars

if __name__=="__main__":
    import timeit
    print "izip",timeit.timeit("izipp()", setup="from __main__ import izipp"),''.join(izipp())
    print "grpby",timeit.timeit("grpby()", setup="from __main__ import grpby"),''.join(grpby())
    print "poke",timeit.timeit("poke()", setup="from __main__ import poke"),''.join(poke())
    print "dread2",timeit.timeit("dread2()", setup="from __main__ import dread2"),''.join(dread2())

给我结果:

izip 13.2173779011 acbgihkjloqsuv
grpby 18.1190848351 abcaghijkloaqsuv
poke 11.8500328064 abcaghijkloaqsuv
dread2 9.0088801384 abcaghijkloaqsuv

因此,基本循环似乎比所有列表推导都要快,并且速度最高是groupby的两倍。然而,基本循环更复杂,难以阅读和编写,因此在大多数情况下,我可能会坚持使用groupby()。


恭喜你的解决方案。@AshwiniChaudhary说你的最快,但是dread2更快! - D Read
确实,但你的仍然基于我的。不过看到这么少的改变实际上可以进一步提高性能,这很有趣,所以做得好;) - poke
实际上,在我写我的代码之前,我没有看过你的代码——变量没有太多意义,但这是一个非常简单的问题,所以我理解相似之处。我只是有点烦恼,因为我的贡献比第二好的贡献快25%,但这并没有反映在答案评分中。 - D Read

0
这是一个正则表达式的一行代码:
>>> mylist = ['a', 'a', 'b', 'c', 'c', 'c', 'd', 'a', 'a']
>>> results = [match[0][0] for match in re.findall(r'((\w)\2{1,})', ''.join(mylist))]
>>> results
['a', 'c', 'a']

抱歉,太懒了不想计时。


0
a = ['a', 'a', 'b', 'c', 'c', 'c', 'd']
res=[]
for i in a:
    if a.count(i)>1 and i not in res:
        res.append(i)
print(res)

0

使用enumerate检查是否有连续两个:

def repetitives(long_list)
  repeaters = []
  for counter,item in enumerate(long_list):
    if item == long_list[counter-1] and item not in repeaters:
      repeaters.append(item)
 return repeaters

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