【问题标题】:Data structure for unique element storage唯一元素存储的数据结构
【发布时间】:2016-05-19 12:02:28
【问题描述】:

我正在寻找一个最好执行等于 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);

【问题讨论】:

  • ...我是密集的,还是你在描述哈希?
  • 我敢肯定,你在 Perl 中写的东西不会比用 C 语言编写的内置 keys 运算符更快。我认为你需要解释更多关于你正在做的事情会给你带来不可接受的表现。 keys 是一个微不足道的操作,它只是返回一个预先存在的 C 字符串列表,重新格式化为 Perl 标量
  • @Borodin 返回就绪数组 ref $membersreturn [keys %$seen] 更快。因此,如果 $factory->(members => 1) 的工厂必须非常快,那么 keys 并不是最快的解决方案。
  • keys() 距离 O(1) 很远,更不用说 jm666 写的了。在经常检索元素的情况下,我认为这是一个值得的内存/速度权衡。

标签: list perl unique xs


【解决方案1】:

keyseach 确实慢得惊人。但是,如果您将每个元素存储为哈希值并使用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%     --

【讨论】:

  • 很高兴看到values() 表现得更好,尽管v5.20.3 的性能差距更大(超过20%)。你用的是哪个 perl 版本?
  • 我在 OS X 上使用 5.18。在 Ubuntu 上使用 5.22 我得到 -13% 和 15%。
【解决方案2】:

具有讽刺意味的是,也许Tie::IxHash 的动机是希望以指定的顺序检索散列的键,但它与您想要的一样接近。

the Tie::IxHash implementation 中,键和值存储在数组引用中。 keys 返回一组密钥的副本,但 (tied %hash)->[1] 之类的内容可以让您直接访问它。

删除 Tie::IxHash 中的元素是 O(n)。一种可能的解决方法是用undef 替换值而不是删除它们。也就是更喜欢

$ixhash{$obsolete_key} = undef;

delete $ixhash{$obsolete_key};

或者,如果您能够汇集删除操作——如果您可以组织代码,以便您通常在多个键上同时调用 delete,并且在哈希的其他操作之间调用——那么就有机会改进Tie::IxHash

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多