【问题标题】:Search an array with less time complexity [closed]搜索时间复杂度较低的数组[关闭]
【发布时间】:2017-08-01 16:49:32
【问题描述】:

向所有程序员问好!

我想介绍我的方法来搜索时间复杂度较低的未排序数组(整数/字符串)。线性搜索方法通常用于最坏情况时间复杂度为 O(n) 的未排序数组。

我已经修改了线性搜索条件以检查数组的最后一个元素,从而减少了迭代。请查看此代码并提供您宝贵的反馈。这里在 O(n/2) 中搜索循环的最坏情况时间复杂度。 [编辑 - 通过答案,知道它仍然是 O(n)]

for(int i=0, len=arr.length; i<len; i++){
        if(arr[i].equals(somethingToFind) || arr[--len].equals(somethingToFind)){
            System.out.println("Found");
            break;
        }
    }

此外,我们可以修改 if 语句以包含更多条件,例如检查 arr[i+1] 和 arr[len-1] 并将 i 增加 2 以减少迭代。举个例子!

我想知道上述代码与线性搜索相比是否有任何性能改进。你有什么想法?感谢您的回复!

编辑 - 非常感谢您的精彩回复!是的,我认为额外的条件会降低性能,但只是想澄清一下。 BigONotation 和时间复杂度给出了很好的回应。谢谢! :-)

【问题讨论】:

  • 不,仍然是 O(n)。您无法在小于 O(n) 的时间内搜索未排序的数组。
  • @Kayaman 不正确,用量子计算,你可以
  • @DamianLattenero 是的,但直到 Java 13 才可用。
  • 这样想——你怎么能保证元素存在于未排序的数组中,而不查看数组中的每个元素?无论您如何遍历它,查看数组中的每个元素都是 O(n)。
  • @Thuvi,另一种看待它的方式。给定您的解决方案,如果集合的大小增加 10 倍,则处理时间仍然是 10 倍。线性增长。

标签: java arrays algorithm search time-complexity


【解决方案1】:

完全未知的结构中搜索时,我们不能比线性搜索做得更好。底线是:我们需要查看每个元素才能找到特定元素(yields O(n))。

不管我们是从从左到右还是从从右到左或者从两边甚至从随机位置。如果我们对结构不了解,那么它没关系


但是,如果我们知道某事,例如结构已排序或任何其他类型的特殊分布,那么我们可以利用并利用优势 的。

只有在这种情况下,我们才能开发出更复杂的技术,例如 二分搜索 用于排序结构。


它认为,只要你不限制输入的结构(所以允许每个任意输入结构),那么你总能找到一种特殊的输入,产生n-lookups每个算法。

如果我猜对了,那么您的特殊算法会从两侧处理列表,因此从左侧开始线性搜索,从右侧开始进行线性搜索,最后它们在中间相遇。

对于这种特殊算法,您可以生成所需元素恰好位于中间的输入,然后您又可以得到n-lookups


在这一点上,我认为您可能对Big-O-Notation 的特征感到困惑。

如果您在一次迭代中进行两次比较甚至 100 次比较,那么您不会降低时间复杂度。虽然您的算法最终将只有 n/2 迭代是正确的(因为它在一次迭代中进行 2 次查找),但它会再次产生 n-lookups

还要注意,即使你的算法真的只看n/2元素,那么根据定义,它也会再次出现在O(n)的集合中(你可以忘记所有常量因素,如1/2在 O 表示法中)。

作为一个粗略的概述:在集合O(g(x)) 中的所有函数f(x) 其中f(x) &lt;= C * g(x) 其中C 是任意常数。如果它不适用于所有 x 值,但它最存在一个绑定 x' 从它是真的,所以对于所有 x &gt; x'

例如,这些函数都在O(n):

  • 100 * n
  • 1/200 * n
  • 5 + 6n
  • n - 10000000

【讨论】:

    【解决方案2】:

    正如@Kayaman 所说,搜索未排序的数组总是 O(n)

    这个问题的答案中有一个证明:Array search NP complete

    假设使用您的算法,您将数组的大小加倍。您的算法还需要多少时间?时间加倍。这就是 O(n) 的意思。

    【讨论】:

    • 为清楚起见,提到的问题指出:数组搜索 NP 完成。线性复杂度 O(n) 实际上不是 NP 完备的。该问题的答案中也对此进行了解释
    【解决方案3】:

    假设你写

    if(arr[0].equals(somethingToFind) || arr[1].equals(somethingToFind) || arr[2].equals(somethingToFind) || arr[3].equals(somethingToFind) || arr[4].equals(somethingToFind)){
                System.out.println("Found");
                break;
           }
    

    在最坏的情况下,您的目标位于最后一个索引中。因此它将是 O(n)。

    【讨论】:

    • 这不是 OP 写的,也不是与 OP 的代码进行检查的顺序相同。
    • 作为 OP 给出了 arr[i].equals(somethingToFind) || 的示例arr[--len].equals(somethingToFind),我把它变成了更具体的风格来表明,无论你做什么,在这种搜索中你最终都会得到 O(n)。
    【解决方案4】:

    即使您将 2 个条件放在一个 IF 中,您每次迭代都会访问该数组 2 次。同样在渐近符号中,O(n/2) 仍然等于 O(n)。 因此,在未排序的数组中搜索元素时,没有比 O(n) 更好的结果了。

    【讨论】:

      【解决方案5】:

      您正在做的(不知不觉中)称为循环展开,即复制循环体内的指令以减少迭代次数和循环开销。

      在这种情况下,您所做的完全适得其反,原因有两个:

      • 您将循环退出测试换成捷径——或者,分支预测可能更难一些。

      • 您在两个地方而不是一个地方扫描内存,一个反向扫描。这是缓存不友好的。

      正如其他人所说,在最坏的情况下,不可能在少于 N 次比较中执行此查找。 (最坏的情况发生在搜索到的元素不在列表中。)

      【讨论】:

        猜你喜欢
        • 2017-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-29
        相关资源
        最近更新 更多