数据结构之优先队列:最小索引优先队列,Python代码实现——15

最小索引优先队列(Min index priority queue)

在之前实现的最大优先队列和最小优先队列,他们可以分别快速访问到队列中最大元索和最小元素,但是他们有一 个缺点,就是没有办法通过索引访问已存在于优先队列中的对象,并更新它们。

为了实现这个目的,在优先队列的基础上,学习一种新的数据结构,索引优先队列。

接下来我们以最小索引优先队列举列,最大优先索引队列,有兴趣可以自行实现。

实现思路

实现功能的三个重要数组

  1. 数组items,储存无序插入的元素的数组
  2. 数组pq,假设将items储存的元素进行从小到大排序,并且items对应的原索引依旧保持不变,而pq储存的元素就是items排序过后元素的原索引(参照下图可更好地理解)
  3. 数组qp,qp的元素对应pq中的索引, qp的索引对应pq中的元素(也就对应着未排序的items中的元素,与items元素的索引保持一致)在这里插入图片描述
    这样删除时只需要根据传入的索引在qp中寻找对应的元素就可以跟快地找到了

实现的操作方法

  1. size()获取队列的大小
  2. is_empty()判断队列是否为空
  3. less(x, y)对传入的两个索引对应当前队列的元素进行大小比较
  4. swap(i, j)对传入的两个索引对应当前队列中的元素进行值交换
  5. min_elem_index()获取最小元素的索引
  6. is_index_exist()判断索引在当前队列中是否对应一个元素
  7. insert(index, item)指定索引index处插入一个元素item
  8. delete_min_elem()删除最小元素,并返回最小元素插入队列时的索引
  9. change_item(idx, itm)指定索引idx处,将该处元素替换为itm
  10. swim()上浮排序操作,同之前堆的排序中介绍
  11. sink()下沉排序操作,同之前堆的排序中介绍
Python代码实现
import operator


class IndexMinPriorityQueue:
    def __init__(self, length):
        self.items = [None for _ in range(length)]
        # Ascendingly sort items and memorize the item's relative index in items
        self.pq = [None] + [i if self.items[i] else None for i in range(len(self.items))]
        # Its index is associate with elements in pq, and also syncs with indices of list items
        self.qp = [i if self.pq[i] else None for i in range(len(self.pq))]
        self.N = 0

    def size(self):
        return self.N

    def is_empty(self):
        return self.N == 0

    def less(self, i, j):
        """Compare the given two items in self.items"""
        return operator.lt(self.items[self.pq[i]], self.items[self.pq[j]])

    def swap(self, i, j):
        """But change the position of the two items in the subsidiary pq and qp lists"""
        self.pq[i], self.pq[j] = self.pq[j], self.pq[i]
        self.qp[self.pq[i]], self.qp[self.pq[j]] = i, j

    def min_elem_index(self):
        """Find the minimum element's index"""
        return self.pq[1]

    def is_index_exist(self, index):
        """Judge if the given index is exist in this queue"""
        return self.qp[index] is not None

    def insert(self, index, item):
        """Insert an element associated with the element's index in this queue"""
        if self.is_index_exist(index):
            return
        self.items[index] = item
        self.N += 1
        # Now it isn't a orderly queue
        self.pq[self.N] = index
        self.qp[index] = self.N

        # swim the last element to make list pq ordered
        self.swim(self.N)

    def delete_min_elem(self):
        """Delete the minimum element, and return its index"""
        min_index = self.pq[1]
        # print(f"min_ele: {self.items[min_index]}")
        self.swap(1, self.N)
        self.pq[self.N] = None
        self.qp[min_index] = None
        self.items[min_index] = None
        self.N -= 1
        self.sink(1)
        return min_index

    def change_item(self, idx, itm):
        """Substitute a item which index=idx with a new item which value=itm"""
        self.items[idx] = itm
        k = self.qp[idx]
        self.sink(k)
        self.swim(k)

    def swim(self, index):
        """Move the smaller element up; We should only change order in pq and qp"""
        while index > 1:
            # Compare the current node with its parent node, if smaller, swim up
            if self.less(index, int(index/2)):  # Compare values in items
                self.swap(index, int(index/2))  # But swap the mapping position in pq and qp
            index = int(index/2)

    def sink(self, index):
        """Move the bigger element down; We should only change order in pq and qp"""
        # print(f"SINK: idx:{index} N:{self.N}")
        while 2*index <= self.N:
            index_smaller = 2*index if 2*index+1 > self.N else \
                (2*index if self.less(2*index, 2*index+1) else 2*index+1)
            # print(f"index_smaller: {index_smaller}")
            # print(f"index: {index}")
            if self.less(index, index_smaller):
                break
            self.swap(index, index_smaller)
            index = index_smaller
测试代码
if __name__ == '__main__':
    IMPQ = IndexMinPriorityQueue(10)

    IMPQ.insert(0, 'C')
    IMPQ.insert(1, 'E')
    IMPQ.insert(2, 'A')
    print(f"After three insert list items now is: {IMPQ.items}")
    # print(f"pq: {IMPQ.pq}")
    # print(f"qp: {IMPQ.qp}")
    # print(f"min_elem_index: {IMPQ.min_elem_index()}")

    index, item = 0, 'B'
    IMPQ.change_item(0, 'B')
    print(f"Changed the item in index[{index}] to {item}, items now is {IMPQ.items}")

    index, item = 0, 'V'
    IMPQ.change_item(0, 'V')
    print(f"Changed the item in index[{index}] to {item}, items now is {IMPQ.items}")

    while not IMPQ.is_empty():
        res = IMPQ.delete_min_elem()
        print(f"Pop the minimum element: {res}")

    print(f"After delete all elements , its size: {IMPQ.size()}")

    print(IMPQ.is_index_exist(0))
测试结果
After three insert list items now is: ['C', 'E', 'A', None, None, None, None, None, None, None]
Changed the item in index[0] to B, items now is ['B', 'E', 'A', None, None, None, None, None, None, None]
Changed the item in index[0] to V, items now is ['V', 'E', 'A', None, None, None, None, None, None, None]
Pop the minimum element: 2
Pop the minimum element: 1
Pop the minimum element: 0
After delete all elements , its size: 0
False

这里为了测试结果直观,直接将数组items拿出来了,注意items中的元素是不会改变位置的,实际改变的是用来定位items中元素的pq和qp数组

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值