【问题标题】:python: retrieving ceiling key and floor key in a dictionary or a setpython:在字典或集合中检索天花板键和地板键
【发布时间】:2013-07-25 16:48:59
【问题描述】:

我有一本带有一堆整数键的字典。对于我没有的键,我希望能够在我想要检索的键之前和之后检索最小和最大的键,但那不存在。
Java 中的 Treemap 类有两种方法可以做到这一点:ceilingkey()floorkey()

如何用 python 做到这一点?

例如,我有一个这样的字典:

 { 1: "1", 4: "4", 6: "6" ..., 100: "100" } 

如果我要求密钥1,我将检索"1", 但如果我寻找密钥3,我应该得到KeyError,因此能够得到floor(3) = 1ceil(3) = 4

【问题讨论】:

  • 你能给我们举个例子吗?

标签: python dictionary map tree


【解决方案1】:
def floor_key(d, key):
    if key in d:
        return key
    return max(k for k in d if k < key)

def ceil_key(d, key):
    if key in d:
        return key
    return min(k for k in d if k > key)

我不确定您想如何处理边界条件。请注意,如果您要求的键的地板/天花板低于/高于字典中的任何内容,这将引发异常 (ValueError)。

【讨论】:

  • 这看起来是正确的方法。我猜测是否可以通过使用二进制搜索来优化算法。
  • @marcorossi:是的。我只是将其更改为使用 min() 和 max() 而不是对其进行排序。我认为这样更有效率,嗯?
  • 无需建立列表。只需去掉括号即可使用生成器表达式。
  • @Jblasco O(N) 为每个丢失的键查找,不是那么快。
  • @nofinator 你可以遍历字典本身,d.keys() 没用。
【解决方案2】:

您可以在这里使用bisect模块,如果没有找到key,那么它可以找到O(log N)时间的ceil和floor值。:

>>> import bisect
>>> from random import randint
def get_value(dic, key):
    if key in dic:
        return dic[key]
    else:
        ind = bisect.bisect(keys, key)
        d = {}
        if ind > 0:
            d["floor"] = dic[keys[ind-1]]
        if ind < len(keys):
            d["ceil"] = dic[keys[ind]]
        return d
...     
>>> dic = {randint(0,100) : x  for x in xrange(10)}
>>> dic
{65: 6, 4: 5, 1: 7, 40: 8, 10: 4, 50: 0, 68: 2, 27: 9, 61: 3}

bisect 创建一个排序的键列表:

>>> keys = sorted(dic)
>>> keys
[1, 4, 10, 27, 40, 50, 61, 65, 68]

现在使用函数:

>>> get_value(dic, 4)
5

对于3,ceil 和 floor 都可用:

>>> get_value(dic, 3)
{'ceil': 5, 'floor': 7}

0 小于最小键,所以这将只返回ceil

>>> get_value(dic, 0)
{'ceil': 7}

对于大于最大键的键,仅返回 floor 值:

>>> get_value(dic, 70)
{'floor': 2}

【讨论】:

  • 这很接近,但我想要一个 floorkey() 和 ceilingkey(),因此为我的密钥返回最小的最近 KEY 和最小的最大 KEY。如果我返回一个带有关联值的元组,那就是一个加号。此外,键是未定义的。
  • @marcorossi 对于小于最小键的键,floorkey 应该返回什么?
  • @marcorossi 你说的keys is undefined是什么意思?
  • 您在 bisect.bisect() 中使用了变量键,但键未定义。
  • @marcorossi 我在它下面定义了keys,它是keys = sorted(dic)。将函数贴在上面,使其更具可读性。
【解决方案3】:

这是一个有趣的使用 bisect。

import bisect

def ceiling_key(d, key):
    keys = sorted(d.keys())
    idx = bisect.bisect_left(keys, key)
    if key in d:
        idx += 1
    return keys[idx]

def floor_key(d, key):
    keys = sorted(d.keys())
    idx = bisect.bisect_left(keys, key) - 1
    return keys[idx]


...

>>> ceiling_key(d, 1)
4
>>> ceiling_key(d, 2)
4
>>> ceiling_key(d, 4)
6
>>> ceiling_key(d, 3)
4
>>> ceiling_key(d, 2)
4
>>> ceiling_key(d, 1)
4
>>> ceiling_key(d, 0)
1
>>> ceiling_key(d, 6)
100
>>> floor_key(d, 6)
4
>>> floor_key(d, 7)
6
>>> floor_key(d, 101)
100
>>> floor_key(d, 100)
6

【讨论】:

  • 这很酷。我认为天花板键中有一个错误。 bisect_left 的结果不应该是 +1。
  • 啊,很好。我在测试之前粘贴了该代码,更改后忘记更新答案。谢谢。
  • 如果你每次调用这些函数时都要调用sorted,那么在这里使用bisect 有什么意义。所以,这仍然是O(NlogN) 用于访问任何密钥。
  • 确实如此。排序可能发生在函数调用之外。
  • 你不应该在floor_key中使用bisect_right吗?
猜你喜欢
  • 2015-02-19
  • 2021-08-25
  • 1970-01-01
  • 1970-01-01
  • 2018-11-02
  • 1970-01-01
  • 2020-11-08
  • 2015-07-10
  • 1970-01-01
相关资源
最近更新 更多