继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

优先队列学习:初学者指南

倚天杖
关注TA
已关注
手记 360
粉丝 47
获赞 187
概述

优先队列是一种特殊的队列数据结构,其中每个元素都关联有一个优先级,具有最高优先级的元素将优先被处理。优先队列广泛应用于操作系统、医疗系统和在线排队系统等多个领域,能够确保具有最高优先级的请求或任务优先得到处理。优先队列的学习对于理解和应用这些高效的数据结构至关重要,涵盖其基本概念、应用场景、实现方式以及优缺点分析。

优先队列简介

优先队列是一种特殊的队列数据结构,其中每个元素都关联有一个优先级。与普通队列不同,优先队列中的元素会按照优先级的高低顺序进行排序,具有最高优先级的元素将优先被处理。优先队列的基本操作包括插入元素、删除优先级最高的元素以及更改元素的优先级。

优先队列的基本概念

优先队列通常用于需要按优先级顺序处理元素的场景中。例如,操作系统中的进程调度,医疗系统中的病情紧急程度排序,在线排队系统等。这些场景中,优先队列能够确保具有最高优先级的请求或任务优先得到处理。

优先队列的应用场景

优先队列在多个领域都有广泛应用,涵盖操作系统、网络通信以及算法设计等多个方面。

  • 操作系统中的进程调度:在操作系统中,进程调度器使用优先队列来管理正在等待执行的进程队列。优先级较高的进程会优先获得处理器资源。
  • 医疗系统中的病情紧急程度排序:医院的急诊室可以采用优先队列来管理病人的等待顺序,确保病情最危急的病人优先接受治疗。
  • 在线排队系统:例如银行、票务、餐厅等需要排队的服务,可以通过优先队列实现VIP客户的快速优先服务。VIP客户可以拥有较高的优先级,确保他们能够优先获取服务。

优先队列的数据结构实现

优先队列可以采用多种数据结构来实现,常见的有基于数组的实现和基于二叉堆的实现。

基于数组的实现

基于数组的实现较为简单,但效率较低。通常情况下,数组实现的优先队列会使用一个大小为 n 的数组,其中 n 是队列中元素的数量。每个元素在数组中的位置会根据其优先级进行调整,以确保优先级最高的元素始终位于数组的起始位置。

代码示例

class PriorityQueueArray:
    def __init__(self):
        self.queue = []

    def is_empty(self):
        return len(self.queue) == 0

    def insert(self, item, priority):
        # 将元素和优先级作为元组插入数组
        self.queue.append((item, priority))
        # 对数组进行排序,保持优先级高的元素在前
        self.queue.sort(key=lambda x: x[1], reverse=True)

    def remove_highest_priority(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        return self.queue.pop(0)[0]

    def change_priority(self, item, new_priority):
        for i, (element, priority) in enumerate(self.queue):
            if element == item:
                self.queue[i] = (item, new_priority)
                # 重新排序以更新优先级顺序
                self.queue.sort(key=lambda x: x[1], reverse=True)
                break

基于二叉堆的实现

二叉堆是一种完全二叉树,它能够高效地实现优先队列的基本操作。二叉堆有两种类型:最大堆和最小堆。最大堆用于实现优先级队列,最小堆用于实现最小优先队列。在最大堆中,根节点的值大于或等于其子节点的值,因此根节点始终是优先级最高的元素。

最大堆实现

最大堆通常使用数组来表示。数组的每个元素表示二叉堆中的一个节点。对于父节点和子节点的关系,可以通过简单的数学公式来计算:假设父节点的索引是 i,那么左子节点的索引是 2 * i + 1,右子节点的索引是 2 * i + 2。根节点的索引是 0

代码示例

class MaxHeapPriorityQueue:
    def __init__(self):
        self.heap = []

    def is_empty(self):
        return len(self.heap) == 0

    def insert(self, item, priority):
        self.heap.append((item, priority))
        self._percolate_up(len(self.heap) - 1)

    def remove_highest_priority(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        # 提取根节点
        root = self.heap[0]
        # 将最后一个元素移动到根位置
        self.heap[0] = self.heap[-1]
        self.heap.pop()
        # 调整堆结构
        self._percolate_down(0)
        return root[0]

    def change_priority(self, item, new_priority):
        for i, (element, priority) in enumerate(self.heap):
            if element == item:
                self.heap[i] = (item, new_priority)
                # 调整堆结构
                self._percolate_up(i)
                self._percolate_down(i)
                break

    def _percolate_up(self, index):
        while index > 0:
            parent_index = (index - 1) // 2
            if self.heap[parent_index][1] < self.heap[index][1]:
                self.heap[parent_index], self.heap[index] = self.heap[index], self.heap[parent_index]
                index = parent_index
            else:
                break

    def _percolate_down(self, index):
        while 2 * index + 1 < len(self.heap):
            left_child = 2 * index + 1
            right_child = 2 * index + 2
            max_index = index
            if self.heap[left_child][1] > self.heap[max_index][1]:
                max_index = left_child
            if right_child < len(self.heap) and self.heap[right_child][1] > self.heap[max_index][1]:
                max_index = right_child
            if max_index != index:
                self.heap[index], self.heap[max_index] = self.heap[max_index], self.heap[index]
                index = max_index
            else:
                break

优先队列的常用操作

优先队列支持几种基本操作,包括插入元素、删除优先级最高的元素以及更改元素的优先级。

插入元素

插入新元素时,优先队列会根据元素的优先级调整其在队列中的位置。如果使用最大堆实现,新元素会插入到堆的末尾,然后通过上滤操作(percolate up)调整位置以确保堆的性质。

代码示例

def insert(self, item, priority):
    self.heap.append((item, priority))
    self._percolate_up(len(self.heap) - 1)

删除优先级最高的元素

删除优先级最高的元素是指删除根节点。在最大堆中,根节点始终具有最高的优先级。删除根节点后,将最后一个元素移动到根位置,然后通过下滤操作(percolate down)调整位置以确保堆的性质。

代码示例

def remove_highest_priority(self):
    if self.is_empty():
        raise Exception("Queue is empty")
    root = self.heap[0]
    self.heap[0] = self.heap[-1]
    self.heap.pop()
    self._percolate_down(0)
    return root[0]

更改元素的优先级

更改元素的优先级是指更新元素的优先级,然后根据新优先级重新调整该元素在堆中的位置。如果优先级增加,需要执行上滤操作;如果优先级降低,需要执行下滤操作。

代码示例

def change_priority(self, item, new_priority):
    for i, (element, priority) in enumerate(self.heap):
        if element == item:
            self.heap[i] = (item, new_priority)
            self._percolate_up(i)
            self._percolate_down(i)
            break

实例代码解析

优先队列在不同的编程语言中都有实现。下面分别介绍 Python 和 Java 中的优先队列实现。

Python 中优先队列的实现

Python 提供了内置模块 queue.PriorityQueue,可以轻松地实现优先队列。这个模块提供了常用队列操作的封装。

代码示例

import queue

pq = queue.PriorityQueue()

# 插入元素
pq.put((2, "item1"))
pq.put((1, "item2"))
pq.put((3, "item3"))

# 删除优先级最高的元素
print(pq.get())  # 输出: (1, 'item2')

# 更改元素的优先级
pq.queue.clear()
pq.put((2, "item1"))
pq.put((1, "item2"))
pq.put((3, "item3"))

# 从队列中移除元素并重新插入以更改优先级
item2 = pq.get()
pq.put((0, item2[1]))
print(pq.get())  # 输出: (0, 'item2')
print(pq.get())  # 输出: (2, 'item1')
print(pq.get())  # 输出: (3, 'item3')

Java 中优先队列的实现

Java 提供了 PriorityQueue 类,可以实现优先队列的功能。PriorityQueue 是一个不提供 null 值的优先队列实现,基于优先堆。

代码示例

import java.util.PriorityQueue;

public class PriorityQueueExample {
    public static void main(String[] args) {
        PriorityQueue<PriorityPair> pq = new PriorityQueue<>();

        // 插入元素
        pq.add(new PriorityPair(2, "item1"));
        pq.add(new PriorityPair(1, "item2"));
        pq.add(new PriorityPair(3, "item3"));

        // 删除优先级最高的元素
        System.out.println(pq.poll());  // 输出: PriorityPair{item='item2', priority=1}

        // 更改元素的优先级
        PriorityQueue<PriorityPair> pq2 = new PriorityQueue<>();
        pq2.add(new PriorityPair(2, "item1"));
        pq2.add(new PriorityPair(1, "item2"));
        pq2.add(new PriorityPair(3, "item3"));

        // 从队列中移除元素并重新插入以更改优先级
        PriorityPair item2 = pq2.poll();
        pq2.add(new PriorityPair(0, item2.item));
        System.out.println(pq2.poll());  // 输出: PriorityPair{item='item2', priority=0}
        System.out.println(pq2.poll());  // 输出: PriorityPair{item='item1', priority=2}
        System.out.println(pq2.poll());  // 输出: PriorityPair{item='item3', priority=3}
    }
}

class PriorityPair implements Comparable<PriorityPair> {
    String item;
    int priority;

    PriorityPair(int priority, String item) {
        this.priority = priority;
        this.item = item;
    }

    @Override
    public int compareTo(PriorityPair other) {
        return this.priority - other.priority;
    }
}

优先队列的优缺点分析

优先队列作为一种特殊的数据结构,在不同应用场景中具有独特的优势和局限性。

优点

  1. 高效性:优先队列可以高效地执行插入、删除和更改优先级的操作,时间复杂度通常是 O(log n)。
  2. 灵活性:优先队列可以适应各种优先级的排序方式,如最大堆和最小堆,以满足不同的需求。
  3. 广泛的应用场景:优先队列在操作系统、网络通信、算法设计等多个领域都有广泛的应用,能有效解决实际问题。

缺点

  1. 空间复杂度较高:优先队列的实现需要额外的空间来存储优先级信息,这会导致空间复杂度较高。
  2. 插入和删除操作复杂:虽然插入和删除操作的时间复杂度是 O(log n),但在大规模数据集下,这些操作的效率可能会受到影响。
  3. 随机访问受限:优先队列不支持随机访问操作,只支持按照优先级顺序进行访问,这在某些应用场景中可能受限。

实际应用案例

优先队列在实际应用中具有广泛的用途,这里展示几个典型的应用案例,例如作业调度和最短路径算法。

作业调度

作业调度是操作系统中的重要功能之一,它负责管理进程的执行顺序。进程调度器通常使用优先队列来管理进程队列,其中每个进程都有一个优先级。优先级较高的进程会优先获得处理器资源,从而确保重要进程得到及时处理。

代码示例

import queue

class Process:
    def __init__(self, id, priority):
        self.id = id
        self.priority = priority

    def __repr__(self):
        return f"Process({self.id}, {self.priority})"

pq = queue.PriorityQueue()

# 添加进程
pq.put((1, Process(1, 5)))
pq.put((2, Process(2, 3)))
pq.put((3, Process(3, 7)))

# 调度进程
while not pq.empty():
    priority, process = pq.get()
    print(f"Scheduling Process {process.id} with priority {priority}")

# 输出:
# Scheduling Process Process(2, 3) with priority 2
# Scheduling Process Process(1, 5) with priority 1
# Scheduling Process Process(3, 7) with priority 3

最短路径算法(Dijkstra)

最短路径算法(Dijkstra)是一种常用的图算法,用于计算图中两个节点之间的最短路径。Dijkstra 算法使用优先队列来存储当前节点及其距离,每次选择距离最短的节点进行处理,从而逐步扩展到其他节点,最终找到最短路径。

代码示例

import heapq

def dijkstra(graph, start, end):
    distances = {node: float('inf') for node in graph}
    distances[start] = 0
    priority_queue = [(0, start)]

    while priority_queue:
        current_distance, current_node = heapq.heappop(priority_queue)

        if current_distance > distances[current_node]:
            continue

        for neighbor, weight in graph[current_node].items():
            distance = current_distance + weight

            if distance < distances[neighbor]:
                distances[neighbor] = distance
                heapq.heappush(priority_queue, (distance, neighbor))

    return distances[end]

# 示例图
graph = {
    'A': {'B': 1, 'C': 4},
    'B': {'A': 1, 'C': 2, 'D': 5},
    'C': {'A': 4, 'B': 2, 'D': 1},
    'D': {'B': 5, 'C': 1}
}

print(dijkstra(graph, 'A', 'D'))  # 输出: 3

医疗系统中的紧急程度排序

在医疗系统中,优先队列可以用来管理病人的等待顺序。病情最危急的病人会被赋予最高的优先级,以确保他们能够优先接受治疗。

代码示例

class Patient:
    def __init__(self, id, priority):
        self.id = id
        self.priority = priority

    def __repr__(self):
        return f"Patient({self.id}, {self.priority})"

pq = queue.PriorityQueue()

# 添加病人
pq.put((1, Patient(1, 7)))
pq.put((2, Patient(2, 5)))
pq.put((3, Patient(3, 2)))

# 处理病人
while not pq.empty():
    priority, patient = pq.get()
    print(f"Treat Patient {patient.id} with priority {priority}")

# 输出:
# Treat Patient Patient(3, 2) with priority 2
# Treat Patient Patient(2, 5) with priority 1
# Treat Patient Patient(1, 7) with priority 0

在线排队系统中的VIP处理

在线排队系统中,VIP客户可以享受优先服务。VIP客户被赋予较高的优先级,确保他们能够优先获得服务。

代码示例

class Customer:
    def __init__(self, id, priority, is_vip):
        self.id = id
        self.priority = priority
        self.is_vip = is_vip

    def __repr__(self):
        return f"Customer({self.id}, {self.priority}, {self.is_vip})"

pq = queue.PriorityQueue()

# 添加客户
pq.put((1, Customer(1, 5, False)))
pq.put((2, Customer(2, 3, True)))
pq.put((3, Customer(3, 7, False)))

# 处理客户
while not pq.empty():
    priority, customer = pq.get()
    print(f"Serve {customer.id} with priority {priority}")

# 输出:
# Serve 2 with priority 2
# Serve 1 with priority 1
# Serve 3 with priority 0

优先队列在实际应用中的诸多案例展示了其在处理具有优先级的任务和请求中的高效性和灵活性。通过具体的应用示例,可以更好地理解优先队列在实际场景中的实现和应用。

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP