【发布时间】:2011-03-02 00:17:17
【问题描述】:
我需要查看字符串数组中是否存在重复项,最省时的方法是什么?
【问题讨论】:
我需要查看字符串数组中是否存在重复项,最省时的方法是什么?
【问题讨论】:
我喜欢 Perl 的一个原因是它几乎可以像英语一样阅读。这有点道理。
use strict;
use warnings;
my @array = qw/yes no maybe true false false perhaps no/;
my %seen;
foreach my $string (@array) {
next unless $seen{$string}++;
print "'$string' is duplicated.\n";
}
'false' is duplicated.
'no' is duplicated.
【讨论】:
将数组转换为哈希是最快的方法 [O(n)],尽管它的内存效率很低。使用 for 循环比 grep 快一点,但我不知道为什么。
#!/usr/bin/perl
use strict;
use warnings;
my %count;
my %dups;
for(@array) {
$dups{$_}++ if $count{$_}++;
}
一种节省内存的方法是对数组进行适当的排序并遍历它以寻找相等且相邻的条目。
# not exactly sort in place, but Perl does a decent job optimizing it
@array = sort @array;
my $last;
my %dups;
for my $entry (@array) {
$dups{$entry}++ if defined $last and $entry eq $last;
$last = $entry;
}
这是nlogn 的速度,因为排序,但只需要在%count 中存储重复数据而不是第二个数据副本。最坏情况下的内存使用仍然是O(n)(当所有内容都重复时),但如果您的数组很大并且没有很多重复项,您将获胜。
抛开理论不谈,基准测试表明后者开始在具有高重复百分比的大型阵列(如超过一百万)上失败。
【讨论】:
如果你无论如何都需要uniquified数组,最快还是用重度优化的库List::MoreUtils,然后和原来的结果对比一下:
use strict;
use warnings;
use List::MoreUtils 'uniq';
my @array = qw(1 1 2 3 fibonacci!);
my @array_uniq = uniq @array;
print ((scalar(@array) == scalar(@array_uniq)) ? "no dupes" : "dupes") . " found!\n";
或者,如果列表很大,并且您想在发现重复条目后立即放弃,请使用哈希:
my %uniq_elements;
foreach my $element (@array)
{
die "dupe found!" if $uniq_elements{$element}++;
}
【讨论】:
创建一个散列或一个集合或使用 collections.Counter()。
当您遇到每个字符串/输入时,请检查哈希中是否存在该实例。如果是这样,那就是重复的(对这些做任何你想做的事情)。否则,使用字符串作为键向哈希添加一个值(例如,哦,比如说数字一)。
示例(使用 Python 的 collections.Counter):
#!python
import collections
counts = collections.Counter(mylist)
uniq = [i for i,c in counts.iteritems() if c==1]
dupes = [i for i, c in counts.iteritems() if c>1]
这些计数器是围绕字典(散列映射集合的 Python 名称)构建的。
这是高效的,因为哈希键是索引的。在大多数情况下,键的查找和插入时间是在几乎恒定的时间内完成的。 (实际上 Perl 的“哈希”之所以被称为是因为它们是使用一种称为“哈希”的算法技巧来实现的——一种校验和,因为当输入任意输入时,它的碰撞概率极低。
如果您将值初始化为整数,从 1 开始,那么您可以增加每个值,因为您已经在哈希中找到了它的键。这几乎是最有效的通用字符串计数方法。
【讨论】:
不是直接的答案,但这将返回一个没有重复的数组:
#!/usr/bin/perl
use strict;
use warnings;
my @arr = ('a','a','a','b','b','c');
my %count;
my @arr_no_dups = grep { !$count{$_}++ } @arr;
print @arr_no_dups, "\n";
【讨论】:
my $dupes_found = !! grep { $_ > 1 } values %count; 更有效的测试是使用first 而不是grep。实际上,即使my $dupes_found = @arr == @arr_no_dupes; 也应该可以工作。
请不要询问最省时的方法,除非您有一些特定要求,例如“我必须在一秒钟内对包含 100,000 个整数的列表进行重复数据删除”。否则,您会无缘无故地担心需要多长时间。
【讨论】:
类似于@Schwern 的第二个解决方案,但在sort 的比较函数中更早地检查重复项:
use strict;
use warnings;
@_ = sort { print "dup = $a$/" if $a eq $b; $a cmp $b } @ARGV;
它不会像散列解决方案那样快,但它需要更少的内存并且非常可爱
【讨论】: