Я должен видеть, существуют ли дубликаты в массиве строк, каков самый эффективный временем способ сделать его?
Одна из вещей, которые мне нравятся в 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' дублируется.
'no' дублируется.
Преобразование массива в хэш - самый быстрый способ [ 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)
(когда все дублируется), но если ваш массив большой и дубликатов мало, вы выиграете.
Помимо теории, сравнительный анализ показывает, что последний начинает проигрывать на больших массивах (например, более миллиона) с высоким процентом дубликатов.
Если вам все равно нужен уникальный массив, то быстрее всего использовать сильно оптимизированную библиотеку 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]
Эти счетчики построены на основе словарей (имя Pythons для коллекций хешированных отображений).
Это экономно по времени, потому что хеш-ключи индексируются. В большинстве случаев время поиска и вставки ключей происходит почти за постоянное время. (На самом деле «хеши» 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";