从阵列中删除偶数量的重复项

时间:2018-04-28 23:06:31

标签: arrays perl duplicates

我有一个数组

[ 1, 0, 0, 0, 5, 2, 4, 5, 2, 2 ]

我需要删除大量的重复项。

这意味着,如果一个值在数组中出现偶数次,则将它们全部删除,但如果它出现奇数次,则只保留一个。

上面数组的结果应为

[ 1, 0, 2, 4 ]

我该怎么做?

4 个答案:

答案 0 :(得分:5)

删除重复项通常按如下方式完成:

use List::Util 1.44 qw( uniqnum );

@a = uniqnum @a;

my %seen;
@a = grep { !$seen{$_}++ } @a;

为了实现您的目标,我们只需要链grep来删除其他不需要的元素。

use List::Util 1.44 qw( uniqnum );

@a = uniqnum grep { $counts{$_} % 2 } @a;

my %seen;
@a = grep { !$seen{$_}++ } grep { $counts{$_} % 2 } @a;

my %seen;
@a = grep { ( $counts{$_} % 2 ) && !$seen{$_}++ } @a;

上述解决方案依赖于每个值的计数。为此,我们可以使用以下内容:

my %counts;
++$counts{$_} for @a;

所有在一起:

my ( %counts, %seen );
++$counts{$_} for @a;
@a = grep { ( $counts{$_} % 2 ) && !$seen{$_}++ } @a;

请注意,这些删除重复项的方法会保留元素的顺序(保留第一个副本)。这更有效(O(N))然后涉及sort(O(N log N))以避免产生非确定性的东西。

答案 1 :(得分:3)

这真的不难,而且自己完全没有尝试解决它是非常糟糕的形式。我希望有人发布这样的问题来描述让别人为他们工作的感觉。即使是困难的填字游戏也不会获得大量的解决方案请求,但在这种情况下,您可能会因其他人编写的解决方案而获得报酬?为什么这不是问题?

  • 构建哈希以计算每个值的当前计数

  • 使用$_ % 2确定新的最终计数

  • 将哈希解构为新数组

my $array = [ 1, 0, 0, 0, 5, 2, 4, 5, 2, 2 ];

my @new_array = do {

    my %counts;

    ++$counts{$_} for @$array;

    map {
        ( $_ ) x ( $counts{$_} % 2 )
    } sort { $a <=> $b } keys %counts;
};

use Data::Dump;
dd \@new_array;

输出

[0, 1, 2, 4]

答案 2 :(得分:1)

请参阅评论,了解这种可能的解决方案是如何做到的。

#!/usr/bin/perl

use strict;
use warnings;

my @a = qw(1 0 0 0 5 2 4 5 2 2);

# Move through the array.
for (my $i = 0; $i < scalar(@a); ) {
  # Move through the positions at and ahead of current position $i
  # and collect all positions $j, that share the value at the
  # current position $i.
  my @indexes;
  for (my $j = $i; $j < scalar(@a); $j++) {
    if ($a[$j] == $a[$i]) {
      push(@indexes, $j);
    }
  }

  if (scalar(@indexes) % 2) {
    # If the number of positions collected is odd remove the first
    # position from the collection. The number of positions in the
    # collection is then even afterwards.
    shift(@indexes);
    # As we will keep the value at the current position $i no new
    # value will move into that position. Hence we have to advance
    # the current position.
    $i++;
  }

  # Move through the collected positions.
  for (my $k = 0; $k < scalar(@indexes); $k++) {
    # Remove the element at the position as indicated by the
    # $k'th element of the collect positions.
    # We have to subtract $k from the collected position, to
    # compensate for the movement of the remaining elements to the
    # left.
    splice(@a, $indexes[$k] - $k, 1);
  }
}

print("@a");

答案 3 :(得分:0)

你有很多答案,这是另一个答案:

use strict;
use warnings;
use Data::Dumper;

my $input = [ 1, 0, 0, 0, 5, 2, 4, 5, 2, 2 ];
my $output = dedupe_evens($input);

print Data::Dumper->Dump([$input, $output], ['$input', '$output']);

exit;


sub dedupe_evens {
    my($input) = @_;

    my %seen;
    $seen{$_}++ foreach @$input;
    my @output = grep {
        my $count = delete $seen{$_};  # only want first occurrence
        $count && $count % 2;
    } @$input;

    return \@output;
}

产生此输出(为简洁而重新格式化):

$input  = [ 1, 0, 0, 0, 5, 2, 4, 5, 2, 2 ];
$output = [ 1, 0, 2, 4 ];