在一个整数数组/列表中查找重复项

3
给定一个整数数组/列表,输出其中的重复数字。 同时,我真正想知道的是:哪种方案具有最佳的时间性能?最佳的空间性能?是否可能同时具有最佳的时间和空间性能?只是好奇。谢谢! 例如:给定列表[4,1,7,9,4,5,2,7,6,5,3,6,7],则答案为[4,7,6,5](输出顺序无关紧要)。 我用Python编写了一个使用哈希和二分搜索的解决方案。 下面是我的解决方案之一:
def binarySearch(array, number):
    start = 0
    end = len(array)
    mid = (end + start) // 2
    while (end > start):
        mid = start + (end - start) // 2
        if array[mid] == number:
            return (mid, True)
        elif number > array[mid]:
            if start == mid:
                return (mid + 1, False)
                start = mid
            else:
                end = mid

    return (mid, False)

def findDuplicatesWithHash(array):
    duplicatesHash = {}
    duplicates = []
    for number in array:
        try:
            index,found = binarySearch(duplicates, number)
            if duplicatesHash[number] == 0 and not found: 
                duplicates.insert(index, number)
        except KeyError as error:
            duplicatesHash[number] = 0

    duplicatesSorted = sorted(duplicates, key=lambda tup: tup)
    return duplicatesSorted

这个输入数组的期望输出是什么?[1,1,2,3,4,4,5,5,5,6] - wookie919
"能够同时拥有最佳的时间和空间性能吗?" 好问题!可能不行 ;) - user1511956
@wookie919:[1,4,5] - OhaiMac
3个回答

3

寻找重复项有多种解决方案。鉴于这个问题是完全通用的,可以假设在给定包含 n 个值的列表中,重复项的数量在范围 [0, n/2] 内。

你能想到哪些可能的方法?

  1. Hash Table approach:

    Store values while traversing the list if value already doesn't exist in the hash table. If the value, exists, you have a duplicate.

    Algorithm FindDuplicates(list)
    hash_table <- HashTable()
    duplicates <- List()
    for value in list:
        if value in hash_table:
            duplicates.add(value)
        else:
            hash_table.add(value, true)
    
    • Time: O(n) to traverse through all values
    • Space: O(n) to save all possible values in the hash table.
  2. Sort Array

    Sort the array and traverse neighbour values.

    Algorithm FindDuplicates(list)
    list.sort()
    duplicates <- Set()
    for i <- [1, len(list)-1]:
        if list[i] = list[i-1]:
            duplicates.add(list[i])
    
    • Time: O(n.logn) + O(n) = O(n.logn) to sort and traverse all values
    • Space: O(1) as no extra space created to produce duplicates
  3. Check for every value

    For every value check if the value exists in the array.

    Algorithm Search(i, list):
        for j <- [0, len(list)-1] - [i]:
            if list[j] = list[i]:
                return true
        return false
    
    Algorithm FindDuplicates(list)
    duplicates <- Set()
    for i <- [1, len(list)-1]:
        if Search(i, list):
            duplicates.add(list[i])
    

    Time: O(n^2) number of comparisons are n*n(-1) Space: O(1) as no extra space created to produce duplicates

注意:重复数组的空间不能包含在空间复杂度方程中,因为这是我们想要的结果。
你还能想到其他的吗?

我想到的两个解决方案就是你提到的前两种方法。不幸的是,我的哈希算法肯定出了什么问题,因为与排序数组方法相比,它的性能表现非常糟糕。 - OhaiMac
你所得到的非常小的数据集的结果可能不能够真正反映出算法的时间复杂度。尽管我们可以假设分配哈希表不需要时间,但在某些环境中,创建哈希表并存储元素可能比原地对数组进行排序要慢。请尝试使用更大的数据集,例如10K或一百万个值,并查看您的算法的表现如何。 - p0lAris
我尝试了包含100个元素到1百万个元素的不同大小的元素。当我使用包含10k、100k和1百万个元素的列表时,与排序数组方法相比,我注意到性能明显变慢。 - OhaiMac
您可以将代码作为原帖的编辑添加。我会查看这个问题。 - p0lAris
@OhaiMac 我不确定这段代码是否应该工作。首先,二分查找应该是在排序数组上进行的。据我所知,“duplicates”不是一个排序列表。此外,这里的想法似乎是错误的。您不需要在重复的数组上进行搜索。将此解决方案与我上面提到的哈希表方法进行比较(这是相当标准的),如果哈希表中已经存在该数字,则将其添加到哈希表中。在哈希表中查找数字是O(1),而在排序数组上进行二分查找是O(logn) - p0lAris
也许我对你们每个算法的理解有误,但输出应该只列出唯一的整数。这就是为什么我的哈希算法在“duplicates”列表中进行二分查找,以确保它尚未添加。而且,当它找不到时,它会返回要插入的索引位置。 - OhaiMac

1
找到重复元素与排序非常相似。也就是说,每个元素都需要直接或间接地与所有其他元素进行比较,以查找是否存在重复项。可以修改快速排序算法来输出具有相邻匹配元素的元素,其空间复杂度为O(n),平均时间复杂度为O(n*log(n))。

很好的观点!我想出来的一个解决方案使用了字典和二分查找。不幸的是,那种方法远不如我想出来的另一种方法好,它基本上就是你上面提到的方法。 - OhaiMac

1

获取重复项的一种方法:

l = [4,1,7,9,4,5,2,7,6,5,3,6]
import collections

print([item for item, count in collections.Counter(l).items() if count > 1])

很棒的解决方案!非常简短明了。这比我使用排序想出来的解决方案稍微快一些。不过,这个解决方案的空间复杂度是多少? - OhaiMac

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