Структура данных для хранения уникальных элементов

Я ищу структуру данных, которая должна предпочтительно выполнять равную O (1)? для любого количества элементов при добавлении/удалении / извлечении элементов.

вот некоторые дополнительные рекомендации,

  • извлечение элементов не должно включать медленное keys()
  • элементы должны быть всегда уникальными и определенными
  • порядок элементов не важен
  • добавление или удаление элемента не должно включать итерацию над другими элементы
  • пробелы в извлеченном списке элементов допустимы и могут быть представлены с помощью undef стоимостью

пожалуйста, предложите лучшее решение, чем,

sub uniqArrayFactory {
  my $members = [];
  my $seen = {};
  my $gaps = [];

  return sub {
    my (%arg) = @_;

    return $members if $arg{members};
    my $m;
    if (defined ($m = $arg{del})) {

      return if !$seen->{$m};
      ${ $seen->{$m} } = undef;
      push @$gaps, delete($seen->{$m});
    }
    elsif (defined ($m = $arg{add})) {

      return if $seen->{$m};
      if (@$gaps) {
        $seen->{$m} = pop @$gaps;
        ${ $seen->{$m} } = $m;
      }
      else {
        push @$members, $m;
        $seen->{$m} = ( $members->[-1] );
      }
    }
    return $m;
  };
}

обновление (использования)

my $fa = uniqArrayFactory();

$fa->(add => 10);
$fa->(del => 10);
my $members = $fa->(mebers => 1);

2 ответов


keys и each действительно удивительно медленно. Но если вы храните каждый элемент как значение хэша и используете values, все сделать быстрее. С

use strict;
use warnings;
use Benchmark qw(:all);

my $i;
my $fa;
my %hash;

my %compare = (
  uarray => sub {
    $fa->(add => $i++);
    my $memb = $fa->(members => 1);
    for my $v (@$memb) { next if !defined $v; }
  },
  hash => sub {
    $hash{ $i } = $i;
    for my $v (values %hash) {}
    $i++;
  },
);

$i = 0; $fa = uniqArrayFactory(); %hash = ();
cmpthese(10000, \%compare);

sub uniqArrayFactory {
  my $members = [];
  my $seen = {};
  my $gaps = [];

  return sub {
    my (%arg) = @_;

    return $members if exists $arg{members};
    my $m;
    if (defined ($m = $arg{del})) {

      return if !$seen->{$m};
      ${ $seen->{$m} } = undef;
      push @$gaps, delete($seen->{$m});
    }
    elsif (defined ($m = $arg{add})) {

      return if $seen->{$m};
      if (@$gaps) {
        $seen->{$m} = pop @$gaps;
        ${ $seen->{$m} } = $m;
      }
      else {
        push @$members, $m;
        $seen->{$m} = \( $members->[-1] );
      }
    }
    return $m;
  };
}

Я:

         Rate   hash uarray
hash   3205/s     --    -6%
uarray 3401/s     6%     --

как ни странно, может быть Tie::IxHash, который был мотивирован желанием получить ключи хэша в указанном порядке, настолько близок, насколько вы собираетесь получить то, что хотите.

на на Tie::IxHash реализация, ключи и значения, хранящиеся в массиве ссылок. keys возвращает копию набора ключей, но что-то вроде (tied %hash)->[1] даст вам прямой доступ к нему.

удаление элементов в элементе Tie::IxHash является O (n). Возможно обходным путем для этого было бы заменить значения на undef вместо того, чтобы удалять их. То есть, предпочитая

$ixhash{$obsolete_key} = undef;

to

delete $ixhash{$obsolete_key};

или если бы вы могли объединить свои удаления - если бы вы могли организовать свой код так, чтобы вы обычно вызывали delete на нескольких ключах примерно в то же время и между другими операциями над хэшем-тогда есть возможности для улучшения на Tie::IxHash.