这两天在看《算法导论》,大二的时候上过算法和数据结构的课,但是后来很长一段时间都没有练习算法方面的题目,有些生疏了。所以重拾旧识,顺便在leetcode上练练手。
队列的主要思想是FIFO(fist-in,first-out),队列的应用也非常广泛如:顺序队列,循环队列、阻塞队列、并发队列、优先级队列等。
1.顺序队列
顺序队列基于数组,是一块连续的内存空间,原理极为简单,简单写一下算法。
//--------------Queue---------------------
ENQUEUE(Q,x):
if tail=length(Q)
{
if head=0
{输出“队列内存空间已满!”;return FALSE;}
else{
数据迁移; //即把从head到length(Q)的数据全部往前挪head个
tail=tail-head;
head=0; //队头置为0
}
}
end if;
Q[tail]=x;
tail=tail+1;
return TRUE
//--------------Dequeue---------------------
DEQUEUE(Q):
if head=tail
return NUL; //队列为空
head=head+1;
return Q[head-1];
但是缺点是,在ENQUEUE
中,如果tail=length(Q)还是会进行一次数据迁移,性能有影响。因此,有了下面的循环队列。
2.循环队列
循环队列需要解决的问题是如何处理“假溢出”
,但是会产生新问题,空队条件和满队条件都是head=tail
,存在二义性。
最常用的解决办法是浪费一个单元。
空队判定条件还是head=tail
;
而满队的判定条件则是head=(tail+1)%N
;
算法如下:
//-------------ENQUEUE
Enqueue(Q,x):
if((tail+1)%length(Q)==head) //队列已满
{输出“队列已满”;
return false;
}
Q[tail]=x;
tail=(tail+1)%length(Q);
return TRUE
//-------------DEQUEUE
Dequeue(Q):
if(head==tail) return NULL;
temp=Q[head];
head=(head+1)%length(Q);
return temp;
3.阻塞队列
总结而言,阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。:
1.支持阻塞的插入方法:队列满的时候,阻塞元素的插入流程,直到队列不满,则唤醒;
2.支持阻塞的移除过程:队列空的时候,阻塞获取元素的线程,直到队列里有元素了再唤醒。
阻塞队列常用于消费者和生产者场景,这两个操作提供了下面4个操作
抛出异常:当阻塞队列满时候,再往队列里插入元素,会抛出 IllegalStateException("Queue full")
异常。当队列为空时,从队列里获取元素时会抛出NoSuchElementException
异常 。
返回特殊值:若插入成功,返回true,失败false;移除的时候,队列为空,返回null,否则true(功能类似于普通队列的Enqueue和Dequeue)
一直阻塞:列满时,如果生产者线程往队列里 put 元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。列空时,消费者线程试图从队列里 take 元素,队列也会阻塞消费者线程,直到队列可用。
超时退出:列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。
如果是无界阻塞队列,队列不可能会出现满的情况,所以使用put或offer方法永远不会被阻塞,而且使用offer方法时,该方法永远返回true
JDK7 提供了 7 个阻塞队列:(真真是五花八门啊)
ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列
下面有篇文章写的很好:
https://www.iteye.com/blog/wsmajunfeng-1629354
4.优先队列
又称优先级队列,这个队列在大二数据结构课程设计的时候,有一道题我偷过懒,直接用的某个库里的优先队列。(后面会放上当时的题目)
优先队列的特点:
最大优先队列:无论入队的顺序,当前最大的元素先出列
最小优先队列:无论入队的顺序,当前最小的元素先出列
借助的有力工具:二叉堆
1.入队操作
把要入队的那个元素插到队尾,做上浮操作
2.出队操作
取出首个元素,把最尾的元素提到首部,做下沉操作。
import java.nio.file.Path;
import java.util.Arrays;
public class PriorityQueue{
private int[] array;
private int size;
public PriorityQueue(){
this.array=new int[10];
}
public void Enqueue(int x){
if(size>=array.length){
reSize();//扩容
}
array[size]=x;
size++;
up_operate(size-1);
}
public int Dequeue(){
if(size==0) {return -32768;}
else {
int result_ret = array[0];
array[0] = array[size - 1];
size--;
//下称操作
down_operate(0);
return result_ret;
}
}
//扩容
private void reSize(){
int newsize=2*size;
this.array= Arrays.copyOf(this.array,newsize);
}
private int Left(int x){
return 2*x+1;
}
private int Right(int x){
return 2*x+2;
}
private int Parent(int x){
return (x-1)/2;
}
private void up_operate(int index){
int parent_index= Parent(index);
int min;
//index为奇数,即左孩子
if(index>0) {
if (array[index] < array[Parent(index)]) {
min = index;
} else {
min = Parent(index);
}
//index偶数,即右孩子
if ((index % 2 == 0) && array[min] > array[index - 1]) {
min = index - 1;
}
if (min != Parent(index)) {
int temp = array[Parent(index)];
array[Parent(index)] = array[min];
array[min] = temp;
up_operate(min);
}
}
}
private void down_operate(int index){
int min=index;
if(Left(index)<size){//左孩子存在
if(array[min]>array[Left(index)]){
min=Left(index);
}
}
if(Right(index)<size){
if(array[min]>array[Right(index)]){
min=Right(index);}
}
if(min!=index){
int temp=array[index];
array[index]=array[min];
array[min]=temp;
down_operate(min);}
}
public static void main(String[] args){
PriorityQueue priorityQueue=new PriorityQueue();
priorityQueue.Enqueue(5);
priorityQueue.Enqueue(3);
priorityQueue.Enqueue(6);
priorityQueue.Enqueue(9);
priorityQueue.Enqueue(8);
priorityQueue.Enqueue(6);
priorityQueue.Enqueue(7);
int de;
while(true) {
de = priorityQueue.Dequeue();
if (de != -32768) {
System.out.println("堆顶数据:" + de+"arrays[]:"+Arrays.toString(priorityQueue.array)+"size:"+priorityQueue.size);
}
else{
System.out.println("队列已空!");
break;
}
}
}
}
运行结果:
下面是之前数据结构老师给的题目,顺便贴上来,当时没有好好想优先队列怎么实现,用起来真爽啊哈哈哈。反思!!!
2、公共钥匙盒(必做)(线性表,栈,队列) [问题描述]
有一个学校的老师共用N个教室,按照规定,所有的钥匙都必须放在公共钥匙盒里,老师不能带钥匙回家。每次老师上课前,都从公共钥匙盒里找到自己上课的教室的钥匙去开门,上完课后,再将钥匙放回到钥匙盒中。
钥匙盒一共有N个挂钩,从左到右排成一排,用来挂N个教室的钥匙。一串钥匙没有固定的悬挂位置,但钥匙上有标识,所以老师们不会弄混钥匙。
每次取钥匙的时候,老师们都会找到自己所需要的钥匙将其取走,而不会移动其他钥匙。每次还钥匙的时候,还钥匙的老师会找到最左边的空的挂钩,将钥匙挂在这个挂钩上。如果有多位老师还钥匙,则他们按钥匙编号从小到大的顺序还。如果同一时刻既有老师还钥匙又有老师取钥匙,则老师们会先将钥匙全还回去再取出。
今天开始的时候钥匙是按编号从小到大的顺序放在钥匙盒里的。有K位老师要上课,给出每位老师所需要的钥匙、开始上课的时间和上课的时长,假设下课时间就是还钥匙时间,请问最终钥匙盒里面钥匙的顺序是怎样的?
[基本要求] 输入格式 输入的第一行包含两个整数N, K。 接下来K行,每行三个整数w, s,
c,分别表示一位老师要使用的钥匙编号、开始上课的时间和上课的时长。可能有多位老师使用同一把钥匙,但是老师使用钥匙的时间不会重叠。
保证输入数据满足输入格式,你不用检查数据合法性。 输出格式
输出一行,包含N个整数,相邻整数间用一个空格分隔,依次表示每个挂钩上挂的钥匙编号。 样例输入 5 2 4 3 3 2 2 7 样例输出
1 4 3 2 5 样例说明
第一位老师从时刻3开始使用4号教室的钥匙,使用3单位时间,所以在时刻6还钥匙。第二位老师从时刻2开始使用钥匙,使用7单位时间,所以在时刻9还钥匙。
每个关键时刻后的钥匙状态如下(X表示空): 时刻2后为1X345; 时刻3后为1X3X5; 时刻6后为143X5;
时刻9后为14325。 课程设计要求: (1)要求从文本文件中输入;
(2)根据时间进程,将取走钥匙和归还钥匙分别视为事件,放入队列中,然后通过每个事件的先后发生对钥匙盒的状态进行变更;
(3)严格按照要求的输入输出格式进行数据的输入、输出(训练CSP考试中的格式化输入输出的正确性);
(4)选做:通过图形界面来显示钥匙盒的即时状态,以及事件队列的状态。
这是当时的代码
#include<stdio.h>
#include<stdlib.h>
#include<queue>
using namespace std;
#include<iostream>
using namespace std;
#include<fstream>
struct node{
int number;//钥匙编号
int situation;//状态,1表示上课前借钥匙,0表示下课后借钥匙
int time;//时间节点;
bool operator <(const node &u) const///按时间从小到大排序//优先队列
{ if(time>u.time)
return true;
else
return false;
}
};
priority_queue<node> q;
int a[10000];//钥匙盒
int main()
{
fstream File;
File.open("C:\\Users\\THINKPAD\\Desktop\\数据结构实验\\课设\\file2.txt",ios::in|ios::binary);
if(File.fail())
{cout<<"cannot open file2\n"<<endl;
exit(0);}
int i;
for(i=0;i<10000;i++)
a[i]=i;//初始化;
int n,k,w,s,c;
//scanf("%d %d",&n,&k);
File>>n>>k;
cout<<n<<" ";
cout<<k<<endl;
node p;
for(int j=0;j<k;j++)
{ //scanf("%d %d %d",&w,&s,&c);
File>>w;cout<<w<<" ";
File>>s;cout<<s<<" ";
File>>c;cout<<c<<endl;
p.number=w;
p.situation=1;//判断借钥匙
p.time=s;
q.push(p);
p.number=w;
p.situation=0;//判断还钥匙
p.time=s+c;
q.push(p);
}
while(!q.empty())
{p=q.top();
q.pop();
if(p.situation==1)
{ for(i=1;i<=n;i++)
{if(p.number==a[i])
{a[i]=-1;break;}
}
}
else
{ for(i=1;i<=n;i++)
{if(a[i]==-1)
{a[i]=p.number;break;}
}//for
}//else
}//while
cout<<"<<钥匙盒的最终结果为:>>"<<endl;
for(i=1;i<=n;i++)
printf("%d ",a[i]);
return 0;
}
这是JAVA中队列应用的一篇文章
https://www.cnblogs.com/yuansc/p/9087044.html