面试 - 编写程序以删除偶数元素

时间:2012-05-26 14:48:03

标签: c# algorithm search

我今天被问到这个问题,我知道答案很简单,但是他让我一直到最后。

问题

编写一个程序,删除存储在包含ArrayList的{​​{1}}中的偶数。

我刚说了哇

这就是我实现它的方式。

1 - 100

扭曲

他告诉我这个计算复杂性是什么给他一个等式。我刚刚说过,这是线性与N(输入)成正比。

他说:嗯...这意味着我需要等待更长时间才能获得结果,当输入大小增加时,我是对的吗? ArrayList source = new ArrayList(100); for (int i = 1; i < 100; i++) { source.Add(i); } for (int i = 0; i < source.Count; i++) { if (Convert.ToInt32(source[i]) % 2 ==0) { source.RemoveAt(i); } } //source contains only Odd elements

为我调整它,尽可能多地尝试Yes sirr you are。我在这部分失败了。

  • 因此,来这里是为了做正确的逻辑,答案或算法。

注意:他不想要Linq,没有额外的花里胡哨。只需简单的循环或其他逻辑就可以了

7 个答案:

答案 0 :(得分:5)

我敢说复杂性实际上是O(N ^ 2),因为数组中的删除是O(N),并且可能会为每个项目调用它。

所以你有O(N)遍历数组(列表)和O(N)每次删除=&gt; O(N)* O(N)。

由于看起来不太清楚,我将解释推理。在每个步骤中,可以移除物品(假设必须移除每个物品的最坏情况)。在阵列中,移除是通过移位完成的。因此,要删除第一个项目,我需要将所有以下N-1项目向左移动一个位置:

1 2 3 4 5 6...
<---
2 3 4 5 6...

现在,在每次迭代中我需要移位,所以我做N-1 + N-2 + ... + 1 + 0次移位,得到(N) * (N-1) / 2(算术系列)的结果,给出O(N ^ 2)的最终复杂度

答案 1 :(得分:4)

让我们这样想:

您正在执行的删除操作的数量是数组长度的一半(如果元素存储在数组中)。所以复杂性至少是O(N)。

你收到的问题让我想你的教授想要你推断存储这些数字的不同方法。

通常,当您具有日志复杂性时,您正在使用不同的结构,如图形或树。

我能想到具有逻辑复杂性的唯一方法是将数字存储在树中(有序树,b树...我们对此进行了详细阐述),但实际上它超出了考试的限制(在数组中输入数字。)

对你有意义吗?

答案 2 :(得分:2)

如果保留两个索引,一个到当前读取位置,另一个到当前写入位置,则性能会明显提高。

int read = 0
int write = 0;

这个想法是read依次查看数组的每个成员; write跟踪列表的当前结束。当我们找到想要删除的成员时,我们会向前移动,但不会写入。

for (int read = 0; read < source.Count; read++) {
  if (source[read] % 2 != 0) {
    source[write] = source[read];
    write += 1;
  }
}

然后在最后,告诉ArrayList它的新长度是`write'的当前值。

这会将你从原来的O(n ^ 2)下降到O(n)。

(注意:我没有测试过这个)

答案 3 :(得分:1)

如果你真的必须使用ArrayList并主动删除条目(相反,如果不是首先添加它们)

不按i + 1递增,但i + 2将无需检查是否为奇数。

for (int i = source.Count - 1 ; i > 0; i = i i 2)
{
   source.RemoveAt(i);
}

修改:我知道只有当source按顺序包含1-100中的条目时才会有效。

答案 4 :(得分:1)

在不更改数据结构或对项目在ArrayList中存储的方式做出一些假设的情况下,我无法看到您将如何避免检查每个成员的奇偶校验(因此至少O(n)复杂度)。也许面试官只是想让你告诉他这是不可能的。

答案 5 :(得分:1)

给定解决方案的问题在于它从头开始,因此每次删除项目时都必须移动整个列表:

Initial List:       1, 2, 3, 4, 5, ..., 98, 99
                         /  /  /  ///  /
After 1st removal:  1, 3, 4, 5, ..., 98, 99, <empty>
                            /  ///  /   /
After 2nd removal:  1, 3, 5, ..., 98, 99, <empty>, <empty>

我使用斜杠试图显示每次删除后列表的移动方式。

您可以通过撤消删除顺序来降低复杂性(并消除我在评论中提到的错误):

for (int i = source.Count-1; i >= 0; --i)  {
  if (Convert.ToInt32(source[i]) % 2 == 0) {
    // No need to re-check the same element during the next iteration.
    source.RemoveAt(--i);
  }
}

答案 6 :(得分:1)

IF 可以为您提供无限制的并行线程。

假设我们有一个包含n元素的数组。为每个元素分配一个线程。假设所有线程都完美同步。

  1. 每个线程决定其元素是偶数还是奇数。 (时间O(1)。)
  2. 确定数组中它下面有多少元素是奇数。 (时间O(log(n))。)
    1. 在第二个数组中标记0或1,具体取决于您在同一索引处是偶数还是奇数。所以每一个都是那个位置的赔率。
    2. 如果您的索引是奇数,请添加上一个数字。现在每个条目都是当前2个自己的块中的赔率计数
    3. 如果你的索引mod 4是2,在下面的索引处添加值,如果是3,则在下面添加答案2索引。现在每个条目都是当前4个区块中的赔率计数。
    4. 使用2**i块继续此模式(如果您在上半部分添加下半部分的计数)log2(n)次 - 现在此数组中的每个条目都是下面的赔率计数
  3. 每个CPU都将其值插入正确的插槽中。
  4. 将数组截断为正确的大小。
  5. 我愿意打赌,这样的事情就是你朋友心中的答案。