关于算法:如何从python中的列表中删除重复的条目

How to remove duplicate entries from a list in python

最近在一次采访中,我被要求编写一个python代码来从列表中删除所有重复的条目。

例如:

1
2
Input List = {1,2,4,5,2,3,1}
Expected Output List = {4,5,3}

在上面的示例中,1和2出现多次,应将其删除。秩序的保存是很重要的。这就是问题所在。

他又一次不希望我使用内置函数,如set()、unique()等。我猜他是在测试我的算法和ds技巧。他在一开始就讲清楚了。

我想了两种方法。1.)排序(nlog(n)的复杂性)2.)哈希表(排序更快)

哈希表方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
arr = [1,2,4,5,2,3,1]

//function : to create a hash table with key = arr[i] & value = occurence count
def dataCountTable(arr):
    countTable = {}
    i = 0
    while i<len(arr) :
        if arr[i] in countTable :
            countTable[arr[i]] += 1
        else :
        countTable[arr[i]] = 1
    i+=1
return countTable

//function : to remove duplicates using the arr & hash table
def rmvAllDuplicate(arr, countTable):
    outList = list()
    i = 0
    while i<len(arr) :
        if countTable[arr[i]] == 1 :
            outList.append(arr[i]);
    i+=1
return outList

print rmvAllDuplicate(arr, dataCountTable(arr))

面试官似乎对这个答案没有印象。它让我一直在寻找更好的围裙。我找不到。

如果有人能帮助我改进我的解决方案,或者建议一个新的更好的解决方案,那就太好了!

谢谢!


我将使用collection.Counter来实现这一点(或自己实现一个):

1
2
3
4
5
6
7
8
9
from collections import Counter
input_list = [1,2,4,5,2,3,1]
# expected_output_list = {4,5,3}

# make Counter object for list elements
# and pick up to list only those values for which count is 1
singles = {x for x, count in Counter(input_list).items() if count == 1}
# filter your list to get only elements that were not duplicates
result = [x for x in input_list if x in singles]

或者正如@forstru指出的那样,您可以:

1
result = [x for x, count in Counter(input_list).items() if count == 1]

但在这种情况下,您不能保证保留您的列表的顺序(h/t@dsm)

这具有线性时间复杂性。


虽然哈希表的实现可以变得更简洁、易读、更惯用,但速度会稍微提高一点,但我怀疑这并不是你的面试官失望的地方。

更可能的是,他推动你寻求一个更好的解决方案,希望你能提出一个论点,为什么你的解决方案实际上是最佳的,但相反,你搜索了一段时间,放弃了。

所以,这里有很多事情要证明:

  • 这个问题的任何解决方案都必须是O(N)时间。
  • 您的解决方案是(摊销、平均和几乎总是)O(n)时间。
  • 解决方案时间复杂性中的乘数是合理的。
  • 任何解决这个问题的O(n)时间必须是O(m)空间(其中m是不同值的数目)。
  • 您的解决方案是O(M)空间。
  • 解决方案空间复杂性的乘数是合理的。
  • 即使是那些简单的,你也不会在面试中拿出真正严格的证据。其中一些人,你甚至不能提出令人信服的理由,但提出可能的例外,承认你在哪里挥手可能就足够了。例如:

    • python的dictset的最坏情况时间为o(n);这只是o(1)的摊余平均情况。您的数据是否有任何可能比O(1)更糟的地方?可能不是,但是……如果这是有人想要做的服务器的一部分,并且他们可以发送他们想要的任何输入数据,那该怎么办?
    • 他给你的所有值都是小整数。这是真的吗?在这种情况下,不要用口述来计算,只要用list(range(0, P)),其中P是最大数。然后是O(P)空间,这听起来比O(M)更糟,只是乘数要小得多——列表占空间的1/3(只是值,而不是哈希、键和值),所以如果P << M/3是一个胜利。这也可能是速度上的胜利,因为不需要保持散列值。你可以更好地使用array.array
    • python散列表对于存储集合和小计数的dict来说是多余的。自定义哈希表是否可以显著地减少数据量,或者不足以使其值?


    我猜如果不允许使用内置函数,也不允许使用stdlib类。否则,一定要用瓦索夫斯基先生的回答。

    但你能自己做同样的事吗?

    当然,一个Counter只是一个花哨的dict而已。您可以实现自己的Counter,或者直接执行相同的工作:

    1
    2
    3
    4
    5
    input_list = [1,2,4,5,2,3,1]
    counts = {}
    for value in input_list:
        counts.setdefault(value, 0)
        counts[value] += 1

    现在和他的其他代码一样:

    1
    2
    singles = {x for x, count in counts.items() if count == 1}
    result = [x for x in input_list if x in singles]

    这实际上与您在"哈希表方法"中所做的相同。但它更简洁,更易读,更惯用,更快,一个小但非零常数,所以它可能仍然给面试官留下了更深刻的印象。(当然,所有这些对瓦索夫斯基先生的版本来说都更为真实。)


    您可以在一行中理解列表:

    1
    2
    3
    in_list = [1,2,4,5,2,3,1]
    out_list = [num for num in in_list if in_list.count(num) == 1]
    # result: [4,5,3]


    尝试:简单

    1
    2
    l=[1,2,4,5,2,3,1]
    [x for x in l if l.count(x)==1 ]

    它将删除所有唯一的项目


    python中的set是一个没有重复项的集合。您可以获取一个列表并删除重复的项目,方法是将其转换为set,然后返回到一个列表:

    1
    2
    3
    4
    l = [1,2,3,2,4,3]
    l = list(set(l))
    print l
    output: [1,2,3,4]


    遍历列表,并为您在列表中看到的特定元素设置一个标记。如果您再次遇到同一个元素,标记将已经设置,您不想再将该元素添加到列表中。这将导致一个线性时间算法。这就是面试官对你的解决方案不满意的原因。哈希实际上也有相同的作用,但您正在为维护哈希表创建一个巨大的重载。

    1
    2
    3
    4
    5
    6
    7
    8
    def f(seq):
       seen = {}
       result = []
       for item in seq:
           if item in seen: continue
           seen[item] = 1
           result.append(item)
       return result