在列表中找到一个下降。这可以在 O(log n) 中完成吗?

Posted

技术标签:

【中文标题】在列表中找到一个下降。这可以在 O(log n) 中完成吗?【英文标题】:Finding a dip in a list. Can this be done in O(log n)? 【发布时间】:2015-05-18 01:42:35 【问题描述】:

我有一个整数列表,我试图通过使用递归算法来识别整数列表中的下降来实现 O(log n)。倾角是任何数字,其后紧跟一个等于或大于自身的数字,并且紧随其后,使得 x >= dip

我已经能够通过简单地遍历列表来实现 O(n),但是我正在尝试使用类似于二进制搜索算法的方法来获得更快的结果。我只需要在列表中找到一个下降。

我的问题是,当我将列表分成中点的左侧和右侧时,我最终会到达带有一/两个元素的较小列表,但它们不一定是坑,因为它们没有考虑到它们切片之外的数字。

谁能帮帮我?

def find_dip(lst):
     if len(lst) == 1:
          return 0
     elif len(lst) == 2:
          if lst[0] <= lst[1]:
               return 0
          else:
               return 1
     else:
          ans = False
          mid = len(lst) // 2
          print("test")
          if lst[mid-1] >= lst[mid] <= lst[mid+1]:
               ans = mid
          elif ans == False and len(lst[mid:]) > 2:
               if lst[mid] >= lst[mid+1] <= lst[mid+2]:
                    ans = mid+1
          elif ans == False and len(lst[:mid]) > 2:
               if lst[mid-2] >= lst[mid-1] <= lst[mid]:
                    ans = mid-1      
          elif ans == False:
               ans = find_dip(lst[:mid])
          else:
               ans = find_dip(lst[mid+1:])
          return ans

【问题讨论】:

如果您添加您编写的代码块,您的问题将在此处得到更好的接收,即使它们不符合您的性能要求。 我不确定您是否可以通过任意输入实现 O(log n) 时间。例如,二进制搜索只能以对数方式工作,因为您知道输入已排序。在这种情况下,线性可能是您的下限。 在最坏的情况下绝对不可能得到 O(log n),因为在某些时候可能必须检查所有元素(因为列表没有属性,所以无法获取信息关于它的元素而不先阅读它们):最坏的情况必须是 O(n)。 【参考方案1】:

答案是肯定的。基于以下link 找到一个峰值(与下降正好相反)需要O(log n) 时间。但区别是肤浅的,所描述的算法可以以完全相同的方式进行调整。这是一个python版本:

def find_dip(lst):
    start, end = 0, len(lst) - 1
    while start < end:
        if end - start == 1: return start if lst[start] <= lst[end] else end
        mid = (start + end) / 2
        a, b, c = lst[mid - 1: mid + 2]
        if a >= b <= c: return mid
        if a <= b: end = mid - 1
        else: start = mid + 1
    return end

【讨论】:

不错。关键的“技巧”是两个端点的“dip”定义。这意味着(a)至少一个端点必须是倾角,您可以在 O(1) 时间内检查,或者(b)两个端点都有一个较低的邻居,这意味着必须有一个倾角 它们之间的某个地方——因为如果没有,那只能意味着列表无限长。在第二种情况下,如果你在两者之间的某个地方选择一个新点来尝试,你会发现,同样,它要么是(a)下降;要么是下降。或 (b) 必须至少有一个较低的邻居,在这种情况下,您可以从“一侧”重新开始。【参考方案2】:

由于您只对找到任何单一的下降感兴趣,因此二进制搜索方法将起作用,并进行一些调整。

i) 不要在每次迭代时检查中间元素,而是检查中间两个(相邻值)。

ii) 比较这两个“中点”并选择较小的那个,以及与该选定值位于中点同一侧的集合的一半。

iii) 重复此操作,就像使用常规二分搜索一样。

iv) 当您检查的集合下降到一个值时,您就会发现自己的下降。

例如: 1,2,3,4,6,2,8,3,6,9,0,2

i) 中间两个值是 2 和 8,如上图粗体所示。

ii) 2

1,2,3,4,6,2

iii) 重复。

尽情享受编码吧! ^.^

注意:如果您遇到相等的中点对,将会有一些小问题。然后,您需要将它们与第三个相邻术语进行比较。如果该项也等于其他两项,则取三项中的一项作为倾角。否则,选择集合中值较小的一半,返回步骤(i)继续迭代。

【讨论】:

以上是关于在列表中找到一个下降。这可以在 O(log n) 中完成吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在排序链表上应用二进制搜索 O(log n)?

二分搜索 O(log n) 算法在顺序列表中查找重复项?

什么会导致算法具有 O(log n) 复杂度?

是否会以O(n ^ 2)复杂度运行循环中的变量?

一次通过在 O(n) 时间内从大量 URL 列表中找到唯一的 URL

在 O(nlogk) 时间内找到长度为 n 的列表中总和小于 t 的 k 个元素