文章目录

  • 优先队列
  • C++用法
  • 声明
  • 基本操作
  • Java用法
  • 声明
  • 基本用法
  • Python
  • heapq
  • PriorityQueue
  • Go
  • 手写一个最大堆


优先队列

通常是语言内已经实现好了的一种数据结构
首先,优先队列显然是一种“队列”,而队列(Queue)的特点是“先进先出,后进后出”。
优先队列区别于普通队列的点在于能够对进队的数据自动排序,始终维持着队列从小到大排或者从大到小排。也可以理解为数据的优先级不一样,每次出队都是最高优先级的先出队。
比如维持着从大到小排序的优先队列,能保证把插入的数据从大到小排,每次从队列中取数都是当前队列中最大的。

优先队列的实现通常都是基于(heap)。当一个完全二叉树满足父节点比左右子树都大于等于 或者 都小于等于 的时候,就称为堆。

一般优先队列的插入和删除时间复杂度为O(logn);

C++用法

声明

添加头文件

#include<queue>

通常声明一个优先队列的方法是
priority_queue<类型> 队列名
最常用的几个包括:

//1.当最大堆用,元素按照从大到小排列,注意空格
priority_queue <int,vector<int>,less<int> >Q;
//2.当最小堆用,int等基本元素按照从小到大排列
priority_queue <int,vector<int>,greater<int> > Q;

记住默认的less由小到大,greater由大到小!

// 3.类型是结构体,其中重载了比较符号。更适合放不能直接比较大小的对象
//1)在结构体李重载比较符号
struct a_struct
{
	int x,y;
	bool operator < (const node & a) const
	{
		return x<a.x;
	}
};
priority_queue<a_struct> Q;
//2) 外面单独写比较函数,声明时传入比较函数
struct a_struct
{
	int x, y;
}
struct tmp //重写比较函数
{
    bool operator() (a_structa, a_structb) 
    {
        return a.x < b.x; //大顶堆
    }
};
priority_queue<a_struct, vector<a_struct>, tmp> Q;

基本操作

优先队列的基本操作,和队列差不多,就是插入、查看队首元素、删除队首元素

Q.push(x); // 插入元素,始终维持内部秩序
x = Q.top(); //查看当前队首,或者说是优先级最高的元素
Q.pop(); //删除最高优先级的元素
int s = Q.size(); // 返回优先队列大小
bool e = Q.empty(); //判断队列是否为空

Java用法

Java集合框架中提供了PriorityQueuePriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的,这里主要使用PriorityQueue。

声明

需要导入

import java.util.PriorityQueue

Java时面向对象的,优先队列里放的也是对象,且对象必须可比较,而且不能为空
1.基本用法,默认是从小到大排序的

Queue<String> q = new PriorityQueue<>();
Queue<Integer> queue1 = new PriorityQueue<Integer>();

如果想要从大到小排列呢?就需要自己override比较函数

Comparator<Integer> comparator = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2.compareTo(o1);
        }
    };

Queue<Integer> queue1 = new PriorityQueue<Integer>(comparator )
// 也可以写成内部类的形式
Queue<Integer> queue2 = new PriorityQueue<Integer>(new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2){
	return o2-o1; //和上面的比较是等效的
}
})

2.如果要放其他对象,那必须是可比较的对象,在Java中意味着该对象的类实现Comparator 或者额外传入Comparator对象。

Comparator<Student> comparator = new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return (o1.id - o2.id);
            }
        };

Queue<Student> queue2 = new PriorityQueue<Student>(comparator);
        queue2.add(new Student(2, "B"));
        queue2.add(new Student(1, "A"));
        queue2.add(new Student(3, "C"));

3.其实还支持将ArrayList转化为PriorityQueue,当然也得是可以比较的

ArrayList<Integer> list = new ArrayList<>();
        list.add(4);
        list.add(0);
        
PriorityQueue<Integer> queue3 = new PriorityQueue<>(list);

基本用法

Q.offer(object);
// 插入,基本等同于Q.add(object),不过add插入失败会异常,offer只是返回false
o = Q.poll(); // 获取而且删除队首元素
o = Q.peek();//获取但不删除队首元素
Q.isEmpty(); //是否为空
Q.size(); //大小

Python

提供了两种结构,一个是heapq一个是PriorityQueue

heapq

import heapq

把列表转化为堆:

a_list = [1,2,2,9,3,4]
heapq.heapify(a_list)

基本操作

heapq.heappush(q, 1) # 插入
heapq.heappop(heap) # 删除并返回最小的元素,用heap[0]可以访问而不删除
heapq.heapreplace(heap, item)
# 弹出并返回 heap 中最小的一项,同时推入新的 item。

默认是最小堆。想要最大堆最简单的操作是list中所有数据取反,这样的最小堆其实是原来数字的最大堆。
自己重写比较函数要重写不少,可以参考 链接 有一些未公开的最大堆的方法,但是并不能保证新加入的数据还能维持最大堆,详见链接

PriorityQueue

这个其实就是基于heapq来实现的,并且线程安全(也就是有锁开销),不过接口更舒服

from Queue import PriorityQueue

pq = PriorityQueue()

for i in range(3,0,-1):
    pq.put(i)

while not pq.empty():
    print pq.get()

基本用法

Q.put(object)
Q.get()
Q.empty()
Q.qsize()

Go

有heap包实现堆的操作,不过优先队列就得自己用heap实现一个,具体可以参考 链接

手写一个最大堆

我们用C++自己实现一个构建堆和维护堆,以最大堆为例。
堆虽然逻辑上是二叉树结构,但是不需要真的存成二叉树。以数组就可以标识。从索引0开始放root节点,父节点的索引为i,则左子树为2i+1,右子树为2(i+1)。最后一个非叶子节点的索引为(size-2)/2(毕竟堆是完全二叉树,前面不会出现空了节点的)。
构造堆:
基本意思是,从最后一个非叶子节点开始,把每个非叶子节点都调整成比它的两个子节点要大

void build_heap(int arr[], int size){
	for(int i = (size-2)/2; i>=0; i--){
		heap_adjust(arr, i, size);
	}
}

调整最大堆

void heap_adjust(int arr[], int i, int size){
	if(i<=(size-2)/2){ // 只有非叶子节点才有调整的必要
		int left = 2*i+1;
		int right = 2*i+2;
		int maxx = i;
		// 找到左右子节点中更大的那个
		if(left < size && arr[left]>arr[maxx])
		{
			maxx = left;
		}
		if(right < size && arr[right] > arr[maxx){
			maxx = right;
		}
		if(maxx != i){
			swap(arr[i], arr[maxx]);
			heap_adjust(arr, maxx, size);//递归处理
		}
	}
}

然后就可以用这个做堆排序了!