使用Perl检查数据数组中重复项的最有效方法是什么?

时间:2010-06-10 05:39:20

标签: arrays perl

我需要查看字符串数组中是否有重复项,这是最省时的方法吗?

7 个答案:

答案 0 :(得分:27)

我喜欢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.

答案 1 :(得分:23)

将数组转换为哈希是最快的方法[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)(当所有内容都重复时),但是如果您的阵列很大并且没有太多重复项,那么您将获胜。

除了理论之外,基准测试显示后者开始在大型阵列(如超过一百万)上丢失,并且重复次数很高。

答案 2 :(得分:7)

如果您还需要无需数组的数组,最快使用经过大量优化的库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}++;
}

答案 3 :(得分:6)

创建哈希或集合或使用 collections.Counter()

当您遇到每个字符串/输入检查以查看哈希中是否存在该实例时。如果是这样,它就是重复的(做你想做的任何事情)。否则,使用字符串作为键,将值(例如,哦,比如数字1)添加到哈希值。

示例(使用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]

这些计数器是围绕字典构建的(用于散列映射集合的Pythons名称)。

这是时间有效的,因为散列键被索引。在大多数情况下,键的查找和插入时间是在接近恒定的时间内完成的。 (事实上​​,Perl“哈希”是所谓的,因为它们是使用一种称为“哈希”的算法技巧实现的 - 一种校验和选择,因为它在输入任意输入时具有极低的冲突概率。)

如果将值初始化为整数,从1开始,则可以在散列中找到其键时递增每个值。这是计算字符串的最有效的通用方法。

答案 4 :(得分:2)

不是直接答案,但这将返回没有重复的数组:

#!/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";

答案 5 :(得分:1)

除非您有一些特定的要求,否则请不要问最有效的方法,例如“我必须在一秒钟内重复删除100,000个整数列表”。否则,你会担心没事的会花多长时间。

答案 6 :(得分:1)

类似于@ Schwern的第二个解决方案,但是在sort的比较函数中稍早检查重复项:

use strict;
use warnings;

@_ = sort { print "dup = $a$/" if $a eq $b; $a cmp $b } @ARGV;

它不会像散列解决方案一样快,但它需要更少的内存并且非常可爱