【问题标题】:Array list algorithm - Interview数组列表算法 - 面试
【发布时间】:2012-04-26 14:07:38
【问题描述】:

我今天在一次采访中被问到这个问题。我尝试了一个解决方案,但想知道是否有更好的方法来解决这个问题:

问题:我有一个 500,000 个元素的数组列表,因此数组列表中每个元素的值与索引相同。例如:list.get(0) = 0; list.get(1) = 1 ...等等。但只有一个元素与此顺序不同步 [即 list.get(i) != i]。你是怎么找到那个元素的。

我的答案:每次比较 list.get(i) 和 i 时,使用多个线程每个线程处理 arraylist 的特定拼接来迭代列表。找到元素后,设置一些布尔变量以向其他线程指示已找到该元素。

有没有办法在不遍历列表的情况下解决这个问题?还是更好的方法?

【问题讨论】:

  • 没有提示这个数字可能在列表中的位置,这个问题有点无聊。
  • 我认为您必须解释“只有一个元素不同步”的真正含义..这没有意义..请参阅下面的答案。我想如果你移动一个元素,所有剩余的元素都会不同步,不是吗?
  • @dued0r 该元素未被删除。面试官问如何识别与索引值不一样的元素。
  • 我认为这不是要找到一种同时访问不同区域的方法......更多的是减少数据访问......

标签: java algorithm collections


【解决方案1】:

在最坏的情况下,您必须检查每个元素,因此您无法提高 O(n) 的时间复杂度。

考虑到这一点,最好的算法是从头到尾扫描数组列表。这样您就可以充分利用可用的内存带宽。

我并不完全清楚线程是如何或为什么进入画面的。似乎不合时宜。这是问题的一部分吗?

【讨论】:

  • 虽然我同意你的观点,但这样的问题是相当枯燥的。
  • @aix 不,这是我对面试官的回答。我在想使用数组拼接和处理数组列表的迭代实际上会更好,但后来我意识到即使使用多个线程也无法改进 O(n)。事实上线程只会引入更多的复杂性。
  • 虽然您可以在同一个循环中从列表的两端解决问题。
  • @NickHolt:嗯,有很多方法可以尝试部分展开该循环。不过,它需要分析才能说出任何有意义的性能影响(当然它不会改变O(n))。
  • 是的,如果内存 I/O 是瓶颈,添加线程将无济于事。大多数 CPU 都有一个外部存储器接口,因此多核上的更多线程无济于事。
【解决方案2】:

答案是:一次迭代。你提到并发原因是他们正在寻找的东西。

其实从 java 8 开始,并行与否的解决方案很简单。我认为最多会带来:

OptionalInt foundInt = IntStream.range(0, list.size())
    .parallelStream()
    .filter(i -> i != list.get(i))
    .findAny();

【讨论】:

    【解决方案3】:

    你不能比O(n)做得更好。

    其次,我认为在这些问题中讨论线程和多线程是个坏主意。他们根本不感兴趣。最后,你有一个 O(whatever) 的运行时,无论如何你的常量都会被删除。

    也许面试官的意思是一个排序数组,其元素从 0 到 n-1,索引从 0 到 n-1。然后将一个元素移动到不同的位置。但这意味着所有剩余的元素都有不同的索引!在这种情况下,您可以使用二分搜索改进搜索:

    然后就可以得到O(log n)中的元素了。从中间开始,检查索引是否等于元素。如果相等,则对上半部分做同样的事情,如果不相等,则使用另一部分。

    【讨论】:

    • 我同意。而如今,500 000 甚至对于单个线程迭代来说都不算多
    • 您的O(log n) 算法似乎在1, 0, 2, 3, 4, 5 列表中失败。你的意思不是移动,而是删除?然后就可以了。
    • @btilly 我认为他的意思是,如果一般规则是 f(n+1)=f(n)+1 (除了那个特定的目标),那么二进制搜索在这里是理想的。
    • @btilly:为什么它在这个列表中失败了?我会在 O(log n) 中找到元素 0。在这种情况下,每个元素都有不同的索引。所以我们寻找索引 0...
    • @duedl0r 我将元素 1 移动到元素 0,但没有触及其他所有内容的索引。因此,当您查看中点时,您会发现它没有改变并查看上半部分。
    【解决方案4】:

    除了@aix 的回答,每个循环做 2 次检查怎么样:

    for (int i = 0; i < list.size / 2; i++)
    {
      if (i != list.get(i))
      {
        return i;
      }
      else if (list.size - i != list.get(list.size - i)
      {
        return list.size - i;
      }
    }
    

    【讨论】:

    • 我的观点是,在没有性能基准的情况下,循环展开最好留给 JIT 编译器。
    • @aix 是的,我同意,纯粹就可读性而言,我会避免做这样的事情。
    【解决方案5】:
    1. iterate through the list 
    2. check for the condition in the elements
    3. when that only element found break out the loop... 
    

    我不认为线程进入竞技场......

    【讨论】:

      【解决方案6】:
      ArrayList<Integer> s = new ArrayList<Integer>();
      
      for (int i=0; i<500000; i++) {
          s.add(i);
      }
      
      s.set(13, 500002);
      
      for (int j=0; j<s.size(); j++) {
          if (j != s.get(j)) {
              System.out.println(j + " " + s.get(j));
          }   
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-03
        • 1970-01-01
        • 1970-01-01
        • 2021-10-14
        • 1970-01-01
        相关资源
        最近更新 更多