《Redis应用实例》书摘(21):优先队列

优先队列是一种常见的数据结构,这种队列中的每个元素都有一个优先级,而优先级则决定了元素在队列中的出队顺序。

在Redis中实现优先队列最直接的方法就是使用有序集合——通过将优先队列元素映射为有序集合成员,并将元素的优先级映射为有序集合成员的分值,程序就可以通过操纵有序集合来操纵优先队列。

代码清单 CODE_PRIORITY_QUEUE 展示了基于有序集合实现的优先队列程序。


代码清单 CODE_PRIORITY_QUEUE 优先队列 priority_queue.py

def none_or_single_queue_item(result):
    """
    以{item: priority}形式返回result列表中包含的单个优先队列元素。
    若result为空列表则直接返回None。
    """
    if result == []:
        return None
    else:
        item, priority = result[0]
        return {item: priority}

class PriorityQueue:

    def __init__(self, client, key):
        self.client = client
        self.key = key

    def insert(self, item, priority):
        """
        将带有指定优先级的元素添加至队列,如果元素已存在那么更新它的优先级。
        """
        self.client.zadd(self.key, {item: priority}) 

    def remove(self, item):
        """
        尝试从队列中移除指定的元素。
        移除成功时返回True,返回False则表示由于元素不存在而导致移除失败。
        """
        return self.client.zrem(self.key, item) == 1

    def min(self):
        """
        获取队列中优先级最低的元素及其优先级,如果队列为空则返回None。
        """
        result = self.client.zrange(self.key, 0, 0, withscores=True)
        return none_or_single_queue_item(result)

    def max(self):
        """
        获取队列中优先级最高的元素及其优先级,如果队列为空则返回None。
        """
        result = self.client.zrange(self.key, -1, -1, withscores=True)
        return none_or_single_queue_item(result)

    def pop_min(self):
        """
        弹出并返回队列中优先级最低的元素及其优先级,如果队列为空则返回None。
        """
        result = self.client.zpopmin(self.key)
        return none_or_single_queue_item(result)

    def pop_max(self):
        """
        弹出并返回队列中优先级最高的元素及其优先级,如果队列为空则返回None。
        """
        result = self.client.zpopmax(self.key)
        return none_or_single_queue_item(result)

    def length(self):
        """
        返回队列的长度,也即是队列包含的元素数量。
        """
        return self.client.zcard(self.key)

以下代码展示了上述优先队列程序的具体使用方式:

>>> from redis import Redis
>>> from priority_queue import PriorityQueue
>>> client = Redis(decode_responses=True)
>>> queue = PriorityQueue(client, "PriorityQueue")
>>> queue.insert("Job A", 100)  # 元素入队
>>> queue.insert("Job C", 200)
>>> queue.insert("Job B", 250)
>>> queue.insert("Job E", 280)
>>> queue.insert("Job D", 330)
>>> queue.length()  # 查看队列长度
5
>>> queue.max()  # 查看优先级最高的元素
{'Job D': 330.0}
>>> queue.min()  # 查看优先级最低的元素
{'Job A': 100.0}
>>> queue.pop_max()  # 弹出优先级最高的元素
{'Job D': 330.0}
>>> queue.pop_min()  # 弹出优先级最低的元素
{'Job A': 100.0}

Tip

本文摘录自《Redis应用实例》一书。
欢迎访问书本主页以了解更多Redis使用案例:huangz.works/rediscookbook/
../_images/rediscookbook-banner.png