【问题标题】:Python - Get largest number in heap that is less than nPython - 获取堆中小于n的最大数
【发布时间】:2015-11-10 09:37:13
【问题描述】:

我在 Python 中找不到以下功能:

给定一组数字,返回小于或等于n 的最大数字,如果不存在这样的数字,则返回None

例如,给定列表 [1, 3, 7, 10]n = 9,该函数将返回 7。

我正在寻找类似于Java's TreeSet.lower 的 Python 功能。

我可以使用另一种数据结构。堆似乎合适。

O(n) 解决方案对于问题的规模来说太慢了。我正在寻找 O(log n) 的解决方案。

背景

我正在处理https://www.hackerrank.com/challenges/maximise-sum。可能的值范围是 1 - 10^14,所以使用带二分查找的排序列表太慢了。

我目前的想法是直接迭代Python's heapq backing array。我希望可能会有更多 Pythonic 的东西。

【问题讨论】:

  • 这个问题是要求您支持对同一数据进行重复搜索吗?对于单个搜索,将无序列表转换为另一个数据结构几乎肯定已经是 O(n)。
  • 是的,这个问题是一个最大子序列问题。所以对于n 元素,我需要使用上面描述的lookup。所以,我真的需要lookup 花费 O(log n) 时间来支持 O(n log n) 的整体复杂度。

标签: python algorithm heap dynamic-programming


【解决方案1】:

我认为您可以为此使用 bintrees 库:https://bitbucket.org/mozman/bintrees/src

例子:

tree = bintrees.RBTree()
In [10]: tree.insert(5,1) 
In [11]: tree.insert(6,1) 
In [12]: tree.insert(10,1)
tree.ceiling_item(5)  -> (5,1)

这个操作的复杂度是O(logN)

【讨论】:

  • 是的,我想使用几个很好的库。不幸的是,我无法使用 HackerRank 上的库。
【解决方案2】:
nextLowest  = lambda seq,x: min([(x-i,i) for i in seq if x>=i] or [(0,None)])

用法:

t = [10, 20, 50, 200, 100, 300, 250, 150]
print nextLowest(t,55)
> 50

我从similar question 获取上述解决方案。

【讨论】:

  • 我做了一些计时。 t = xrange(1,10000000) print nextLowest(t,10000000-1) 在我的机器上需要 3.6 秒。
【解决方案3】:

如果你不能对数组的顺序做出任何假设,那么我认为你能做的最好的就是 O(n):

def largest_less_than(numlist, n):
    answer = min(numlist, key=lambda x: n-x if n>=x else float('inf'))
    if answer > n:
        answer = None
    return answer

如果问题是关于在同一数据集上重复获得不同 n 值的最大小于值,那么也许一种解决方案是使用 bucket sort 让您的列表按 O(n) 排序,然后使用反复bisect

【讨论】:

  • 我做了一些计时。 n = 10000000-1 num_list= xrange(1,10000000) 在我的机器上需要 2.1 秒。
【解决方案4】:

您可以为此使用selection algorithm。下面我为此提供了一个简单的算法:

numbers = [1, 3, 7, 10]
n = 9
largest_number = None
for number in numbers:    
    if number<=n:
        largest_number=number
    else:
        break

if largest_number:
    print 'value found ' + str(largest_number)
else:
    print 'value not found'

【讨论】:

  • 我做了一些计时。 n = 10000000-1 numbers = xrange(1,10000000) 在我的机器上需要 2.3 秒。
【解决方案5】:

如果你不必支持从列表中动态添加和删除,那么只需对其进行排序并使用二进制搜索在 O(log N) 时间内找到最大的

【讨论】:

    【解决方案6】:

    ig-melnyk 的回答可能是完成这个问题的正确方法。但是由于 HackerRank 没有使用库的方法,所以这里是我用于解决问题的 Left-Leaning Red Black Tree 的实现。

    class LLRB(object):
    
        class Node(object):
            RED = True
            BLACK = False
    
            __slots__ = ['value', 'left', 'right', 'color']
    
            def __init__(self, value):
                self.value = value
                self.left = None
                self.right = None
                self.color = LLRB.Node.RED
    
    
            def flip_colors(self):
                self.color = not self.color
                self.left.color = not self.left.color
                self.right.color = not self.right.color
    
    
        def __init__(self):
            self.root = None
    
    
        def search_higher(self, value):
            """Return the smallest item greater than or equal to value.  If no such value
            can be found, return 0.
    
            """
            x = self.root
            best = None
            while x is not None:
                if x.value == value:
                    return value
                elif x.value < value:
                    x = x.left
                else:
                    best = x.value if best is None else min(best, x.value)
                    x = x.right
    
            return 0 if best is None else best
    
        @staticmethod
        def is_red(node):
            if node is None:
                return False
            else:
                return node.color == LLRB.Node.RED
    
    
        def insert(self, value):
            self.root = LLRB.insert_at(self.root, value)
            self.root.color = LLRB.Node.BLACK
    
    
        @staticmethod
        def insert_at(node, value):
            if node is None:
                return LLRB.Node(value)
    
            if LLRB.is_red(node.left) and LLRB.is_red(node.right):
                node.flip_colors()
    
            if node.value == value:
                node.value = value
            elif node.value < value:
                node.left = LLRB.insert_at(node.left, value)
            else:
                node.right = LLRB.insert_at(node.right, value)
    
    
            if LLRB.is_red(node.right) and not LLRB.is_red(node.left):
                node = LLRB.rotate_left(node)
            if LLRB.is_red(node.left) and LLRB.is_red(node.left.left):
                node = LLRB.rotate_right(node)
    
            return node
    

    【讨论】:

      【解决方案7】:

      您可以减少要查找的数字,直到找到为止。

      这个函数将找到最大数

      如果没有小于或等于 n 的数字,则返回 -1。

      def findmaxpos(n):
        if n < fs[0]: return -1  
        while True:
          if n in fs: return fs.index(n)
          n-=1
      
      

      【讨论】:

        猜你喜欢
        • 2023-01-25
        • 2020-02-18
        • 1970-01-01
        • 2020-09-05
        • 1970-01-01
        • 2023-01-17
        • 2018-04-14
        • 2012-01-06
        • 2016-01-29
        相关资源
        最近更新 更多