从lis中删除相邻的重复元素

2024-05-14 09:04:32 发布

您现在位置:Python中文网/ 问答频道 /正文

Google Python类|列表练习-

Given a list of numbers, return a list where all adjacent == elements have been reduced to a single element, so [1, 2, 2, 3] returns [1, 2, 3]. You may create a new list or modify the passed in list.

我使用新列表的解决方案是-

def remove_adjacent(nums):
  a = []
  for item in nums:
    if len(a):
      if a[-1] != item:
        a.append(item)
    else: a.append(item)        
  return a

这个问题甚至表明可以通过修改传入列表来完成。但是,python文档警告不要在使用for循环迭代列表时修改元素。

我想知道除了遍历列表之外,我还能尝试什么来完成这项工作。我不是在寻找解决办法,但也许一个提示可以带我进入一个正确的方向。

更新

-用建议的改进更新了上述代码。

-使用建议的提示尝试使用while循环执行以下操作-

def remove_adjacent(nums):
  i = 1
  while i < len(nums):    
    if nums[i] == nums[i-1]:
      nums.pop(i)
      i -= 1  
    i += 1
  return nums

Tags: in列表forlenreturnifdefitem
3条回答

以下是传统的方法,在原地删除相邻的重复项,同时向后遍历列表:

Python 1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)] on win32
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> def dedupe_adjacent(alist):
...     for i in xrange(len(alist) - 1, 0, -1):
...         if alist[i] == alist[i-1]:
...             del alist[i]
...
>>> data = [1,2,2,3,2,2,4]; dedupe_adjacent(data); print data
[1, 2, 3, 2, 4]
>>> data = []; dedupe_adjacent(data); print data
[]
>>> data = [2]; dedupe_adjacent(data); print data
[2]
>>> data = [2,2]; dedupe_adjacent(data); print data
[2]
>>> data = [2,3]; dedupe_adjacent(data); print data
[2, 3]
>>> data = [2,2,2,2,2]; dedupe_adjacent(data); print data
[2]
>>>

更新:如果您想要一个生成器,但是(没有itertools.groupby或(您键入的速度可能比读取其文档和了解其默认行为的速度还快),这里有一个六行程序:

Python 2.3.5 (#62, Feb  8 2005, 16:23:02) [MSC v.1200 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> def dedupe_adjacent(iterable):
...     prev = object()
...     for item in iterable:
...         if item != prev:
...             prev = item
...             yield item
...
>>> data = [1,2,2,3,2,2,4]; print list(dedupe_adjacent(data))
[1, 2, 3, 2, 4]
>>>

更新2:关于巴洛克风格itertools.groupby()和极简主义object()。。。

要从itertools.groupby()中获得重复数据消除相邻效果,需要围绕它包装一个列表理解,以丢弃不需要的分组:

>>> [k for k, g in itertools.groupby([1,2,2,3,2,2,4])]
[1, 2, 3, 2, 4]
>>>

。。。或者用itertools.imap和/或operators.itemgetter乱搞,如另一个答案所示。

使用object实例的预期行为是,没有一个实例与任何类的任何其他实例进行比较,包括object本身。因此他们是非常有用的哨兵。

>>> object() == object()
False

值得注意的是,itertools.groupbyPython reference code使用object()作为哨兵:

self.tgtkey = self.currkey = self.currvalue = object()

当你运行代码时,它会做正确的事情:

>>> data = [object(), object()]
>>> data
[<object object at 0x00BBF098>, <object object at 0x00BBF050>]
>>> [k for k, g in groupby(data)]
[<object object at 0x00BBF098>, <object object at 0x00BBF050>]

更新3:前向索引原位操作说明

OP的修订代码:

def remove_adjacent(nums):
  i = 1
  while i < len(nums):    
    if nums[i] == nums[i-1]:
      nums.pop(i)
      i -= 1  
    i += 1
  return nums

最好写成:

def remove_adjacent(seq): # works on any sequence, not just on numbers
  i = 1
  n = len(seq)
  while i < n: # avoid calling len(seq) each time around
    if seq[i] == seq[i-1]:
      del seq[i]
      # value returned by seq.pop(i) is ignored; slower than del seq[i]
      n -= 1
    else:
      i += 1
  #### return seq #### don't do this
  # function acts in situ; should follow convention and return None

使用生成器迭代列表中的元素,并且yield只有在列表发生更改时才使用新的元素。

^{}正是这样做的。

如果在副本上迭代,则可以修改传入列表:

for elt in theList[ : ]:
    ...

这里再说明一个没有索引的单行程序版本:

def remove_adjacent(nums):
     return [a for a,b in zip(nums, nums[1:]+[not nums[-1]]) if a != b]

not部分将最后一个值放入result,因为只有a结束于result。

相关问题 更多 >

    热门问题