【问题标题】:List comparison列表比较
【发布时间】:2010-09-09 06:16:43
【问题描述】:

我在面试中使用这个问题,我想知道最好的解决方案是什么。

编写一个 Perl 子程序,它接受 n 个列表,然后返回 2^n-1 个列表,告诉您哪些项目在哪些列表中;即,哪些项目仅在第一个列表、第二个列表、第一个和第二个列表以及所有其他列表组合中。假设 n 相当小(小于 20)。

例如:

list_compare([1, 3], [2, 3]);
  => ([1], [2], [3]);

这里,第一个结果列表给出了仅在列表 1 中的所有项目,第二个结果列表给出了仅在列表 2 中的所有项目,第三个结果列表给出了两个列表中的所有项目。

list_compare([1, 3, 5, 7], [2, 3, 6, 7], [4, 5, 6, 7])
  => ([1], [2], [3], [4], [5], [6], [7])

这里,第一个列表给出了仅在列表 1 中的所有项目,第二个列表给出了仅在列表 2 中的所有项目,第三个列表给出了同时在列表 1 和列表 2 中的所有项目,如第一个例子。第四个列表给出了仅在列表 3 中的所有项目,第五个列表给出了仅在列表 1 和 3 中的所有项目,第六个列表给出了仅在列表 2 和 3 中的所有项目,第七个列表给出了所有项目在所有 3 个列表中。

我通常将此问题作为 n=2 的问题子集的后续问题。

解决办法是什么?

跟进:列表中的项目是字符串。可能有重复项,但由于它们只是字符串,因此应在输出中压缩重复项。输出列表中项目的顺序无关紧要,列表本身的顺序很重要。

【问题讨论】:

    标签: list perl


    【解决方案1】:

    您给定的解决方案仍然可以简化很多。

    在第一个循环中,您可以使用普通加法,因为您只对单个位进行 ORing,并且您可以通过迭代索引来缩小 $bit 的范围。在第二个循环中,您可以从索引中减去 1,而不是生成需要关闭 shifted 的不必要的第 0 个输出列表元素,并且您不必要地迭代 m*n 次(其中 m 是输出列表的数量和n 是唯一元素的数量),迭代唯一元素会将迭代减少到只有 n(这在 m 远大于 n 的典型用例中是一个显着的胜利), 将简化代码。

    sub list_compare {
        my ( @list ) = @_;
        my %dest;
    
        for my $i ( 0 .. $#list ) {
            my $bit = 2**$i;
            $dest{$_} += $bit for @{ $list[ $i ] };
        }
    
        my @output_list;
    
        for my $val ( keys %dest ) {
            push @{ $output_list[ $dest{ $val } - 1 ] }, $val;
        }
    
        return \@output_list;
    }
    

    另请注意,一旦采用这种方式,结果收集过程可以借助 List::Part 模块非常简洁地编写:

    use List::Part;
    
    sub list_compare {
        my ( @list ) = @_;
        my %dest;
    
        for my $i ( 0 .. $#list ) {
            my $bit = 2**$i;
            $dest{$_} += $bit for @{ $list[ $i ] };
        }
    
        return [ part { $dest{ $_ } - 1 } keys %dest ];
    }
    

    但请注意,list_compare 是一个可怕的名字。像part_elems_by_membership 这样的东西会好得多。此外,Ben Tilly 指出的问题中的不精确之处需要纠正。

    【讨论】:

    • 不按位执行或意味着如果输入列表中有重复项,则解决方案将失败。然而,为输出列表生成反转循环是一个显着的改进。谢谢。
    【解决方案2】:

    首先我想指出,nohat 的回答根本行不通。尝试运行它,并查看 Data::Dumper 中的输出以验证这一点。

    也就是说,你的问题不是很好。看起来您正在使用集合作为数组。您希望如何处理重复项?你想如何处理复杂的数据结构?你想要元素的顺序是什么?为方便起见,我假设答案是重复的,可以对复杂的数据结构进行字符串化,并且顺序无关紧要。在这种情况下,以下是一个完全足够的答案:

    sub list_compare {
      my @lists = @_;
    
      my @answers;
      for my $list (@lists) {
        my %in_list = map {$_=>1} @$list;
        # We have this list.
        my @more_answers = [keys %in_list];
        for my $answer (@answers) {
          push @more_answers, [grep $in_list{$_}, @$answer];
        }
        push @answers, @more_answers;
      }
    
      return @answers;
    }
    

    如果您想调整这些假设,则需要调整代码。例如,不压缩复杂数据结构和不压缩重复数据可以通过以下方式完成:

    sub list_compare {
      my @lists = @_;
    
      my @answers;
      for my $list (@lists) {
        my %in_list = map {$_=>1} @$list;
        # We have this list.
        my @more_answers = [@$list];
        for my $answer (@answers) {
          push @more_answers, [grep $in_list{$_}, @$answer];
        }
        push @answers, @more_answers;
      }
    
      return @answers;
    }
    

    然而,这是使用数据结构的字符串化来检查存在于一个中的事物是否存在于另一个中。放松这种情况需要更多的工作。

    【讨论】:

    • 抱歉,我并不清楚这个问题。我的意思是每个输出列表都应该包含 only 在输出列表对应的输入列表中的项目。此解决方案与示例中的结果不匹配;它给出了输入列表中的 any 项,这是一个更简单的问题。
    【解决方案3】:

    这是我的解决方案:

    构造一个哈希,其键是输入列表中所有元素的并集,值是位字符串,如果元素存在于列表i中,则设置位i 。位串是使用按位或构造的。然后,通过迭代哈希的键来构造输出列表,将键添加到关联的输出列表中。

    sub list_compare {
        my (@lists) = @_;
        my %compare;
        my $bit = 1;
        foreach my $list (@lists) {
            $compare{$_} |= $bit foreach @$list;
            $bit *= 2; # shift over one bit
        }
    
    
        my @output_lists;
        foreach my $item (keys %compare) {
            push @{ $output_lists[ $compare{$item} - 1 ] }, $item;
        }
    
        return \@output_lists;
    
    }
    

    更新为包含亚里士多德建议的反向输出列表生成

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-08
      • 2012-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多