Python-数据结构与算法(十三、优先队列——基于两种不同的底层实现)

保证一周更两篇吧,以此来督促自己好好的学习!代码的很多地方我都给予了详细的解释,帮助理解。好了,干就完了~加油!
声明:本python数据结构与算法是imooc上liuyubobobo老师java数据结构的python改写,并添加了一些自己的理解和新的东西,liuyubobobo老师真的是一位很棒的老师!超级喜欢他~
如有错误,还请小伙伴们不吝指出,一起学习~
No fears, No distractions.

一、优先队列

  1. 什么是优先队列?
    普通队列:先进先出;后进后出
    优先队列:出队顺序和入队顺序无关;和优先级相关。比如:看病的时候急诊患者和普通患者、操作系统中任务的调度。

  2. 优先队列本质上还是一个队列,所以接口:

# 为了更好的阐明意义,用C++来写的函数声明
void enqueue(E)       	   	# E入队
E dequeue()              	# 出队并获取出队元素
E getFront()              	# 获取队首元素
int getSize()              	# 获取优先队列的大小
bool isEmpty()          	# 判断优先队列是否为空

和普通队列的接口是一样的,只不过出队元素是谁是有讲究的,因为应该先出优先级最高的元素~

  1. 可能用到的底层数据结构分析:
    如果用普通的线性结构来实现优先队列,入队时间复杂度:O(1),然而出队需要对队列进行一次扫描来拿到优先级最高的元素,此时时间复杂度为:O(n)
    如果用排好序的线性结构来实现优先队列(比如优先级最高的元素始终位数组尾部),那么入队的时候就需要找到适当的位置,此时时间复杂度为O(n),相应的出队时间复杂度为O(1)
    所以用以上两种数据结构来实现优先队列都会有O(n)的操作产生,这显然是不理想的,所以就引出了本节所要用到的底层结构——最大堆
    堆的出队与入队操作的时间复杂度都是O(logn)的,性能非常的好。

  2. 只要你想要操作的元素支持比较操作(>,<),就能够放到优先队列中,本节测试用例都是用的正整数作为优先队列中的元素。。。

二、基于最大堆以及数组的优先队列的实现

# -*- coding: utf-8 -*-
# Author:           Annihilation7
# Data:             2018-10-21  2:20 am
# Python version:   3.6

from Priorityqueue.maxheap import MaxHeap  # 从maxheap.py文件中导入我们上一节实现MaxHeap类
from array.Array import Arr # 导入我们以前实现的数组

class PriorityQueue:
    def __init__(self, initial_capacity=20):
        """
        Description: 优先队列的构造函数
        Params:
        initial_capacity: 初始容量,由于底层是我们实现的数组类,有自动扩容,所以后面的操作不用过多的考虑底层。
        """
        self._data = MaxHeap(initial_capacity)   # 初始化我们的最大堆

    def isEmpty(self):
        """
        Description: 判空
        Returns:
        bool值,空为True
        """
        return self._data.isEmpty()   # 直接调用maxHeap的isEmpty就好了

    def getSize(self):
        """
        Description: 获取当前二叉堆中有效元素的个数
        Returns:
        有效元素的个数
        """
        return self._data.getSize()   # 直接调用maxHeap的getSize

    def enqueue(self, elem):
        """
        Description: 将元素elem入队
        时间复杂度:O(logn)
        Params:
        - elem: 待入队元素
        """
        self._data.add(elem)    # 调用MaxHeap的add函数就完事了

    def getFront(self):
        """
        Description: 看一眼队首的元素是谁,也就是优先级最高的元素
        时间复杂度:O(1)
        Returns:
        队首的元素
        """
        return self._data.findMax()   # 堆顶的元素就是优先级最高的元素呀,所以调用MaxHeap的findMax就好了

    def dequeue(self):
        """
        Description: 将队首元素出队,并将其返回
        时间复杂度:O(logn)
        Returns:
        出队元素的值
        """
        return self._data.extractMax()  # 直接调用MaxHeap的extractMax,函数里面有检查了,就不需要判空了



class ArrayPriorityQueue:
	"""基于数组的优先队列的实现,纯粹是为了对比用才实现的,主要还是要掌握基于最大堆的优先队列,这个看看就好"""
    def __init__(self):
        """基于我们以前实现的数组的优先队列的构造函数"""
        self._data = Arr()   

    def isEmpty(self):
        """
        Description: 判空
        Returns:
        bool值,空为True
        """ 
        return self._data.isEmpty()      # 调用数组的isEmpty函数

    def getSize(self):
        """
        Description: 获取当前二叉堆中有效元素的个数
        Returns:
        有效元素的个数
        """
        return self._data.getSize()      # 调用数组的getSize函数

    def enqueue(self, elem):
        """
        Description: 将元素elem入队
        时间复杂度:O(1)
        Params:
        - elem: 待入队元素
        """
        self._data.addLast(elem)         # 直接插入到队尾,此时复杂度为O(1)

    def getFront(self):
        """
        Description: 看一眼队首的元素是谁,也就是优先级最高的元素
        时间复杂度:O(n)
        Returns:
        队首的元素
        """
        return self._data[self._data.get_Max_index()]   # 先获取最大值索引,再得到元素值即可

    def dequeue(self):
        """
        Description: 将队首元素出队,并将其返回
        时间复杂度:O(n)
        Returns:
        出队元素的值
        """
        return self._data.removeMax()   # 直接调用数组的removeMax函数就好

三、性能对比测试

from queue.testQueueAndLoopqueue import count_time  # 导入计算时间损耗的装饰器
from Priorityqueue.priorityqueue import PriorityQueue, ArrayPriorityQueue  # 导入PriorityQueue和ArrayPriorityQueue 
import numpy as np 
np.random.seed(7)

nums = 10000  # 操作数目为10000次
max_heap_PriorityQueue = PriorityQueue()  # 基于最大堆的优先队列的对象
arr_PriorityQueue = ArrayPriorityQueue()  # 基于数组的优先队列的对象

record_list1 = []
record_list2 = []

def check_list(alist, flag):
    """
    Description: 检查输入数组是否是严格的降序排列
    Params:
    - alist: 输入的list
    - flag: bool值,判断测试的是基于最大堆的优先队列还是基于数组的优先队列
    """
    for i in range(1, len(alist) - 1):
        if alist[i] > alist[i - 1]:     
            raise Exception('Error! The list is not absolutely a reversed list!')
    if flag:
        print('Priority Queue which is based on the MaxHeap test completed.')  # True为测试的基于最大堆的优先队列
    else:
        print('Priority Queue which is based on the Array test completed.')    # False为测试的基于数组的优先队列


@count_time
def count_time_maxHeapPriorityqueue():
    global nums, max_heap_PriorityQueue, record_list1
    for i in range(nums):
        max_heap_PriorityQueue.enqueue(np.random.randint(1000))
    for i in range(nums):
        record_list1.append(max_heap_PriorityQueue.dequeue())

@count_time
def count_time_arrPriorityqueue():
    global nums, arr_PriorityQueue, record_list2
    for i in range(nums):
        arr_PriorityQueue.enqueue(np.random.randint(1000))
    for i in range(nums):
        record_list2.append(arr_PriorityQueue.dequeue())


if __name__ == '__main__':
    print('基于最大堆的优先队列用时:')
    count_time_maxHeapPriorityqueue()
    print('基于数组的优先队列用时:')
    count_time_arrPriorityqueue()

    print('检测:')    # 既然每次都在尾部添加优先队列中最大的元素(权重最高的元素),那么list应该是严格降序排列的
    check_list(record_list1, True)   # 检测基于最大堆的优先队列
    check_list(record_list2, False)  # 检测基于数组的优先队列

四、输出

基于最大堆的优先队列用时:
共用时: 0.420652 秒
基于数组的优先队列用时:
共用时: 9.073835 秒
检测:
Priority Queue which is based on the MaxHeap test completed.
Priority Queue which is based on the Array test completed.

五、总结

  1. 可以看到两种不同的底层实现的性能差距还是非常明显的,随着操作量的进一步增多,性能差距将更加明显。一般的优先队列的底层都是基于最大(小)堆来实现的。
  2. 本节只用整数作为测试样例,只是为了方便,小伙伴可以向优先队列中加入任何支持比较操作的对象,那样才能真正的看到优先队列的作用。它的用处真的非常广泛。

若有还可以改进、优化的地方,还请小伙伴们批评指正!

猜你喜欢

转载自blog.csdn.net/Annihilation7/article/details/83218846
今日推荐