【问题标题】:Optimize Algorithm Required- Arrays需要优化算法 - 数组
【发布时间】:2011-09-11 20:04:43
【问题描述】:

我们有一个大小为 N 的整数数组 A。给定另一个包含索引的数组 B,其中 size of B <= N0<=B[i]<=N-1

现在我们必须删除数组 A 中位置 B[i] 的所有元素。

所以删除意味着我们也在移动数组 A 中的元素。

有人可以帮我找到O(n) 解决这个问题的方法吗?并且可能是 O(1) 空间。

我想到的第一个解决方案是,遍历数组B并顺序删除A中的元素(包括移位)但它是O(n^2)

【问题讨论】:

  • B 的元素是否按某种顺序排序?如果您还要移动 A,则 B 中元素的顺序很重要。
  • 你对这些数字有什么限制吗?我的意思是,您可以使用一些特殊值将某些元素标记为“已删除”吗?如果是这样,这将是一件容易的事。
  • @Kiril,不允许将元素标记为已访问/删除:(
  • @SubmittedDenied, B数组中的元素不需要排序。
  • 好吧,不允许使用 O(1) 空间和标记。甚至更多 - B 未排序。我不确定这是否可能。

标签: java c++ c algorithm logic


【解决方案1】:

类似于 iliaden 的解决方案,只是您可以就地删除已删除的元素。

int[] a = 
int[] b = 
int nullValue = 
for(int i: b) a[i] = nullValue;
int j=0;
for(int i=0; i < a.length; i++) {
    if(a[i] != nullValue)
       a[j++] = a[i];
}
// to clear the rest of the array, if required.
for(;j<a.length;j++)
   a[j] = nullValue;

注意:a 不会更短,但会避免创建更多空间。 'j' 将具有a 中的有效条目数

【讨论】:

  • 很好,O(n) 时间和 O(1) 空间。完美的。非常感谢:)
  • 我认为这是尽可能好的,改变数组的大小通常是O(n)内存(创建新数组,复制所有内容)。可能值得检查剩余的元素 N-j 元素并在其中写入空值。
  • 此解决方案取决于 A 的内容具有某种空值。通常情况并非如此。如果没有空值,您必须在某种位向量中维护有关哪些元素被单独删除的信息。上面的第二个循环只是std::remove_if 的一个效率较低的版本。
  • @James,OP 已发布他认为 O(1) 内存分配是可能的,这意味着它应该在不分配与大小成比例的额外内存的情况下完成。以我的经验,在业务逻辑中总是可能出现空值。我看不出std::remove_if 比第二个循环更有效。 (现在我已经将第二个循环移出;)
  • 根据我的经验,空值并不总是可能的;这就是发明Fallible 类(或boost::optional,或Maybe)的原因。但这取决于应用程序,以及他存储在数组中的东西(他没有告诉我们)。如果没有空值,我认为没有额外内存的解决方案;您必须将附加信息存储在某处。至于remove_if,看吧;通常,它使用std::find_if 来查找从哪里开始复制,这样可以避免一些不必要的自分配。
【解决方案2】:

在 O(n) 空间中,你可以这样做:

  1. 遍历数组 A 删除每个 b[i] 处的元素(无移位,O(n))

  2. 新建数组C,复制所有非空元素 从 A 到 C 顺序(也是 O(n))

  3. 返回数组 C 或复制回来 到一个清除的数组 A (O(n)) 上。 因此你可以在 O(n) 时间内完成 和 O(n) 空间

【讨论】:

  • 感谢您的回复。这会起作用,但我正在寻找 O(1) 的空间和 O(n) 的时间。
【解决方案3】:

没有标记,O(n)时间,还有O(n)空格,伪代码:

// create a boolean array indicating which elements are to be deleted
D = new boolean[N]
fill(D, false)
for (b in B) {
  D[b] = true
}

// compact `A in place
src = 0
dest = 0
while (src < N) {
  if (!D[src]) {
    A[dest++] = A[src]
  }
  src++
}

new_N = dest

【讨论】:

    【解决方案4】:

    如果您可以假设 b 已排序,则可以在迭代时移动(如果没有,您可以在 O(n*log(n)) 中对 b 进行排序)

    int[] b;
    int[] a;
    int first=0,bInd=0;
    for(int i = 0;i<a.length;i++){
        if(bInd>=b.length || b[bInd]!=i){
            a[first]=a[i];
            first++;
        }else{
            bInd++;
        }
    }
    

    【讨论】:

    • 不能对B进行排序,因为移动A时A中项目的位置会发生变化。B可能是[0, 0],表示需要删除A中的前两个元素。
    • @submitted 我在这里假设 B 中的元素是数组 A 中的精确索引 在任何删除之前 所以b=[0,0] 要么无效,要么意味着只有第一个应该删除
    • @submittedDenied,排序不会影响真正的解决方案。 B 中的索引是 wrt 初始数组。移位不会对数组 B 产生任何影响。
    • @learner 排序将允许您以从后到前的顺序进行删除。所以删除不会影响任何尚未删除的对象的索引。
    【解决方案5】:

    两个明显的解决方案:在开始之前对B进行倒序排序,这样你 始终删除最高索引(因此永远不要移动已删除的元素), 或遍历B 以创建要删除的元素的位图(和 然后以相反的顺序执行这些操作)。第一个需要额外的 O(lg n) 提前一步,第二个额外的空间。但我不是 肯定有更好的选择。

    【讨论】:

    • B的顺序很重要,不能排序。此外,即使它是按升序或降序排列,您也需要将已删除元素右侧的元素(即具有更高索引的元素)向左移动。
    • @SubmittedDenied 每当您实际从std::vector 中删除一个元素时(除非它是最后一个),您都必须移动其他元素。但是像std::remove_if 这样的函数实际上并没有删除元素。一旦将要保留的元素移动到开头,您就可以在末尾删除一组元素。从后面开始的原因是您不会使未处理的索引无效。如果不能对B进行排序,那么唯一的解决办法就是使用位向量;之后,您可以使用std::remove_iferase,或copy_if
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-27
    • 1970-01-01
    • 2013-01-05
    • 2015-07-17
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    相关资源
    最近更新 更多