【问题标题】:Get first element greater than a given number from a sorted list从排序列表中获取大于给定数字的第一个元素
【发布时间】:2019-05-15 16:08:58
【问题描述】:

我有两个列表。列表 B 就像一个数据库,我需要将列表 A 的每个元素一一进行比较。让我们说

B = [0.6, 1.7, 3, 4.5]
A = [0.6, 0.9, 1.2, 1.5, 2, 2.5, 3, 4, 4.5]

B 是一个排序列表,因此对于每个 A[i],只要算法在 B 中找到 >= A[i] 的数字,它就应该将其作为输出返回。所以我的输出应该是这样的:

C = [0.6, 1.7, 1.7, 1.7, 3, 3, 3, 4.5, 4.5]

您能否建议我最简单的解决方案,尽可能避免嵌套循环?

【问题讨论】:

  • 你的输出 C 没有意义。 B 中的 4.5 大于 A 的前 8 个条目,因此根据您的解释,4.5 应该在 C 中出现 8 次,但您编写的 C 中只有两次 4.5。这也适用于其他几个数字,例如 3
  • @Bazingaa,他需要第一个更大的元素。
  • 如果B = [0.6, 1.7, 3, 3.9] 怎么办?请注意,B 中的最后一个元素小于A 中的最后(两个)元素。

标签: python arrays list numpy sorting


【解决方案1】:

一种递归方式(对原始列表具有破坏性),如果 list_a 包含大于 list_b 的数字也可以使用

def pick(lst, ref, res=None):
  if res == None: res = []
  if len(lst) == 0: return res
  if ref[0] >= lst[0]:
    res.append(ref[0])
    lst.pop(0)
  elif len(ref) == 1 and ref[0] < lst[0]:
    # res.extend(lst) # if want to append the rest of lst instead of stop the loop
    # or do whathever is best for you
    return res
  else: ref.pop(0)
  pick(lst, ref, res)
  return res


list_b = [0.6, 1.7, 3, 3.9]
list_bb = [0.5]
list_a = [0.6, 0.9, 1.2, 1.5, 2, 2.5, 3, 4, 4.5]

print(pick(list_a, list_b))
#=> [0.6, 1.7, 1.7, 1.7, 3, 3, 3]

print(pick(list_a, list_bb))
#=> []

【讨论】:

    【解决方案2】:

    由于B已排序,您可以使用bisectB中的正确值进行二进制搜索:

    >>> B = [0.6, 1.7, 3, 4.5]
    >>> A = [0.6, 0.9, 1.2, 1.5, 2, 2.5, 3, 4, 4.5]
    >>> import bisect
    >>> [B[bisect.bisect_left(B, a)] for a in A]
    [0.6, 1.7, 1.7, 1.7, 3, 3, 3, 4.5, 4.5]
    

    这具有复杂性 O(alogb)ab 的长度分别为 AB。假设 A 也已排序,如您的示例所示,您也可以在 O(a+b) 中进行排序:

    i, C = 0, []
    for a in A:
        while B[i] < a:
            i += 1
        C.append(B[i])
    

    但是请注意,如果 A 包含的数字大于 B 中的任何数字,则这两种方法(以及迄今为止发布的其他答案)都将失败。

    【讨论】:

    • 我们也是这么想的。无论如何,+1
    • @MihaiAlexandru-Ionut 哦,我没看到。在我发布我的答案之前你刚刚编辑了吗?
    • 这值得称赞,它至少在一个输入中矢量化的。 +1
    【解决方案3】:

    如果您可以使用第 3 方库,一个解决方案是 NumPy,通过np.searchsorted

    import numpy as np
    
    B = np.array([0.6, 1.7, 3, 4.5])
    A = [0.6, 0.9, 1.2, 1.5, 2, 2.5, 3, 4, 4.5]
    
    res = B[np.searchsorted(B, A)]
    
    array([ 0.6,  1.7,  1.7,  1.7,  3. ,  3. ,  3. ,  4.5,  4.5])
    

    这将比顺序循环或基于标准库中的bisect 的算法更有效。

    【讨论】:

      【解决方案4】:

      只要next 就可以了(如果我理解正确):

      A = [0.6, 0.9, 1.2, 1.5, 2, 2.5, 3, 4, 4.5]
      B = [0.6, 1.7, 3, 4.5]
      
      C = [next(b for b in B if b >= a) for a in A]
      
      print(C)  # -> [0.6, 1.7, 1.7, 1.7, 3, 3, 3, 4.5, 4.5]
      

      【讨论】:

      • 我喜欢这个列表理解是完全可读和自然的英语,即使它对于较长的列表可能效率低下。
      • @tobias_k 我认为这个问题连同发布的答案完美地展示了 Python 的强大功能。从非常简单、几乎自然的英语到更加复杂和非常有效的方法。
      【解决方案5】:

      由于您给定的B 列表已排序,您可以使用:

      B = [0.6, 1.7, 3, 4.5]
      A = [0.6, 0.9, 1.2, 1.5, 2, 2.5, 3, 4, 4.5]
      
      def first_greater_elem(lst, elem):
          for item in lst:
             if item >= elem:
               return item
      

      然后只需使用列表推导

      C = [first_greater_elem(B,item) for item in A ]
      

      输出

      [0.6, 1.7, 1.7, 1.7, 3, 3, 3, 4.5, 4.5]
      

      另一种方法是使用 bisect 包中的 bisect_left 方法。

      C = [B[bisect_left(B,item)] for item in A ]
      

      输出

      [0.6, 1.7, 1.7, 1.7, 3, 3, 3, 4.5, 4.5]
      

      【讨论】:

        猜你喜欢
        • 2011-01-30
        • 1970-01-01
        • 1970-01-01
        • 2021-09-06
        • 1970-01-01
        • 2014-09-22
        • 2023-03-18
        • 2016-12-31
        • 1970-01-01
        相关资源
        最近更新 更多