【问题标题】:Algorithm to find added/removed elements in an array在数组中查找添加/删除元素的算法
【发布时间】:2010-05-04 11:11:38
【问题描述】:

我正在寻找解决以下问题的最有效方法

问题:

given an array Before = { 8, 7, 2, 1} and an array After ={1, 3, 8, 8}
find the added and the removed elements

the solution is:
        added = 3, 8 
        removed = 7, 2

到目前为止我的想法是:

for i = 0 .. B.Lenghtt-1
{
    for j= 0 .. A.Lenght-1
    {
        if A[j] == B[i]

            A[j] = 0;
            B[i] = 0;

            break;
    }
}

// B elemnts different from 0 are the Removed elements
// A elemnts different from 0 are the Added elemnts

有谁知道更好的解决方案,可能更有效,并且不会覆盖原始数组

【问题讨论】:

  • 如果添加 3、8 并删除 7、2、1,则“数组之后”应为 {3, 8, 8}{1, 3, 8, 8}
  • “After”数组中不能存在“1”。你从一个“1”开始,你删除它,你不加回来,那为什么它在 After 数组中?你应该修正你的例子。
  • 我经常犯的错误,我已经改正了。谢谢,jj

标签: arrays algorithm language-agnostic


【解决方案1】:

排序是你的朋友。

对两个数组(a 和 b)进行排序,然后遍历它们(使用 x 和 y 作为计数器)。一次向下移动 1 个。您可以从那里派生所有测试:

  • 如果 a[x]
  • 如果 a[x] > b[y],则添加 b[y](并且仅增加 y)

(我可能遗漏了一个极端情况,但你明白了。)

(编辑:这里未涉及的主要边缘情况是在您到达其中一个数组的末尾之前处理,但不难弄清楚。:)

【讨论】:

  • 感谢乔、安德烈亚斯和克里斯。只是最后的检查,就效率而言,正确地说您的解决方案成本是 n log(n) + n log(n) + n = O (n log n) 因此,在处理这类问题时,总是建议先排序,对吧? jj
  • @jj:基本上是的,先排序更好。在这种特殊情况下,您还可以避免使用哈希进行排序,因为您实际上并不需要订单,而只需要知道该项目是否在这里。
  • 我也更喜欢哈希,因为它的复杂性较低Theta(N)
  • Joe 和 Andreas,你们是怎么找到这个算法的?您能否阐明您的想法?您是如何识别已排序的数组以及这种扫描方式将被证明是有效的? jj
【解决方案2】:

您也可以使用Dictionary<int, int> 和类似的算法:

foreach i in source_list: dictionary[i]++;
foreach i in dest_list: dictionary[i]--;

最终的字典告诉您插入/删除了哪些元素(以及多久一次)。即使对于更大的列表,此解决方案也应该相当快 - 比排序更快。

【讨论】:

    【解决方案3】:

    如果您的语言作为多集可用(设置元素数量),您的问题是标准操作。

    B = multiset(Before)
    A = multiset(After)
    

    结果是 A.symdiff(B)(symdiff 是联合减交集,这正是您要删除和添加的内容)。

    显然,您也可以仅使用集合之间的经典差异来删除或添加。

    它可以使用散列轻松实现,它是 O(n)(使用排序的效率略低,因为它是 O(n.log(n)),因为排序本身)。

    【讨论】:

      【解决方案4】:

      在某种 C++ 伪代码中:

      Before.sort();
      After.sort();
      int i = 0;
      int j = 0;
      for (; i < Before.size() && j < After.size(); ) {
          if (Before[i] < After[j]) {
              Removed.add(Before[i]);
              ++i;
              continue;
          }
          if (Before[i] > After[j]) {
              Added.add(After[j]);
              ++j;
              continue;
          }
          ++i;
          ++j;
      }
      for (; i < Before.size(); ++i) {
           Removed.add(Before[i]);
      }
      for (; j < After.size(); ++j) {
           Added.add(After[j]);
      }
      

      【讨论】:

        【解决方案5】:

        这可以在线性时间内解决。 创建一个映射来计算每个元素的重复次数。 遍历before 数组并填充地图。 遍历after 数组并减少映射中每个元素的值。 最后,遍历地图,如果找到负值,则添加该元素 - 如果为正,则删除该元素。

        这里有一些 Java 代码(未经测试,只是现在写的):

        Map<Integer, Integer> repetitionMap = new HashMap<Integer, Integer>();
        
        for (int i = 0; i < before.length; i++) {
            Integer number = repetitionMap.get(before[i]);
            if (number == null) {
                repetitionMap.put(before[i], 1);
            } else {
                repetitionMap.put(before[i], number + 1);
            }
        }
        
        for (int i = 0; i < after.length; i++) {
            Integer number = repetitionMap.get(after[i]);
            if (number == null) {
                repetitionMap.put(after[i], -1);
            } else {
                repetitionMap.put(after[i], number - 1);
            }
        }
        
        Set<Integer> keySet = repetitionMap.keySet();
        for (Integer i : keySet) {
            Integer number = repetitionMap.get(i);
            if (number > 0) {
                System.out.println("removed " + number + "times value " + i);
            }
        
            if (number < 0) {
                System.out.println("added " + number + "times value " + i);
            }
        }
        

        【讨论】:

          【解决方案6】:

          perl:

          @a = ( 8, 7, 2, 2, 1 ); @b = ( 1, 3, 8, 8, 8 ); $d{$_}++ for(@a); $d{$_}--for(@b); 打印“添加=”; for(keys %d){print "$_" x (-$d{$_}) if($d{$_}0)} 打印"\n";

          结果:

          $ ./inout.pl 添加 = 8 8 3 删除 = 7 2 2

          【讨论】:

          • 这不是代码高尔夫条目!说真的,perl 在最好的时候是神秘的,所以使用更简单的语言(或伪代码)来说明您希望共享的算法。即使问题微不足道,我也很难理解你在这里做什么。
          【解决方案7】:

          在 Groovy 中:

          def before = [8, 7, 2, 1, 1, 1], after = [1, 3, 8, 8, 8]
          def added = before.countBy{it}
          def result = after.inject(added){map, a -> map[a] ? map << [(a):map[a] - 1]: map << [(a):-1]}
                  .inject([:]){m, k, v -> v == 0 ? (m << [:]) : (v < 0 ? m << [(k):"added ${v.abs()} times"] : m << [(k):"removed ${v.abs()} times"])}
          println "before $before\nafter  $after"
          println "result: $result"
          

          结果:

          before [8, 7, 2, 1, 1, 1]
          after  [1, 3, 8, 8, 8]
          result: [8:added 2 times, 7:removed 1 times, 2:removed 1 times, 1:removed 2 times, 3:added 1 times]
          

          对于countBy,我从Some groovy magic post得到启发

          在常规语言中inject 就像在其他函数式语言中的reduce

          我还推荐Groovy collection api slides from Trygve Amundsen 带有功能方法的非常好的表格

          第二种解决方案:

          def before = [8, 7, 2, 1, 1, 1], after = [1, 3, 8, 8, 8]
          def sb = before.countBy{it}
          def sa = after.countBy{it}
          def result = sa.inject(sb){m, k, v -> m[k] ? m << [(k): m[k] - v] : m << [(k): -v]}
             .inject([:]){m, k, v -> v == 0 ? (m << [:]) : (v < 0 ? m << [(k):"added ${v.abs()} times"] : m << [(k):"removed ${v.abs()} times"])}
          

          【讨论】:

            猜你喜欢
            • 2022-01-23
            • 2018-02-27
            • 2017-01-30
            • 1970-01-01
            • 2018-07-02
            • 1970-01-01
            • 2018-01-16
            • 1970-01-01
            • 2014-04-19
            相关资源
            最近更新 更多