数据结构与算法(五)——队列

队列介绍

队列是一种线性表,它支持 先进先出(FIFO),

尾部添加、头部删除,跟我们生活中的排队类似。

数据结构与算法(五)——队列_2021-03-31-14-28-50.png

队列类型

  1. 单队列

    单队列就是常见的队列, 每次添加元素时,都是添加到队尾:

    单队列会出现“假溢出”现象,每次从队首删除元素,元素都要上前移动一个位置,能放的空位就相对减少了。

  2. 循环队列

    我们可以将队列看成一个首位相连的环。当一个元素从队列删除后,新空余出来的空间可以作为队列的尾部。

    插入的时候,根据实际数据量count与数组长度比较,未满根据数组新增标志putIndex插入数据,如果数组下标临界,设为0重新开始,count++;

    删除的时候,判断有无实际数据count,有的话根据取数据删除标志takeIndex,若takeIndex走到最后,从0开始,count–;

  3. 阻塞队列和并发队列

    阻塞队列其实就是在队列基础上增加了阻塞操作。简单来说,就是在队列为空的时候,从队头取数据会被阻塞。因为此时还没有数据可取,直到队列中有了数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后再返回。

    线程安全的队列我们叫作并发队列。最简单直接的实现方式是直接在 enqueue()、dequeue() 方法上加锁,但是锁粒度大并发度会比较低,同一时刻仅允许一个存或者取操作。实际上,基于数组的循环队列,利用 CAS 原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。

队列应用

  1. 模拟现实世界中的队列,如售票柜台的队列以及其他先到先服务的场景。
  2. 计算客户在呼叫中心等待的时间。
  3. 异步数据的传输(文件输入输出、管道、嵌套字)。
  4. 操作系统中的优先级任务执行。
  5. 短信群体发送 应用的发布订阅模式

java 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
public class Queue {
/***
* 1.单向队列(Queue):只能在一端插入数据,另一端删除数据。
* 2.双向队列(Deque):每一端都可以进行插入数据和删除数据操作。
*
* 与栈不同的是,队列中的数据不总是从数组的0下标开始的
* 选择的做法是移动队头和队尾的指针。
* 为了避免队列不满却不能插入新的数据,我们可以让队尾指针绕回到数组开始的位置,这也称为“循环队列”。
* */
// 单向循环队列,顺序存储结构实现
private Object[] objQueue;
//队列大小
private int maxSize;
//顶部
private int top;
//底部
private int bottom;
//实际元素
private int item;

public Queue(int size) {
maxSize = size;
objQueue = new Object[maxSize];
top = 0;
bottom = -1;
item = 0;
}

/**
* 入队
* @param obj
*/
public void add(Object obj){
if(item == maxSize){
throw new RuntimeException(obj+" add error, queue is full");
}
//循环队列,首尾结合,下标控制队首和队尾位置
if(bottom == maxSize-1){
bottom = -1;
}
objQueue[++bottom] = obj;
item++;
}

/**
* 出对
* @return
*/
public Object out(){
if(item == 0){
throw new RuntimeException("queue is null");
}
Object obj = objQueue[top];
//声明原顶栈可以回收空间(GC)
objQueue[top] = null;
top++;
//重置下标
if(top == maxSize){
top = 0;
}
item--;
return obj;
}

//链式存储结构实现
private class NodeQueue<Object>{
private Object data;

private NodeQueue next;

public NodeQueue(Object data, NodeQueue next) {
this.data = data;
this.next = next;
}
}
//队列头 出
private NodeQueue queueTop;
//队列尾 进
private NodeQueue queueBottom;
//队列大小
private int size;

public Queue() {
queueTop = null;
queueBottom = null;
size = 0;
}

/**
* 入队
* @param obj
*/
public void addNodeQueue(Object obj){
if(size == 0){
queueTop = new NodeQueue(obj,null);
//指向同一存储地址
queueBottom = queueTop;
}else{
NodeQueue<Object> nodeQueue = new NodeQueue(obj,null);
//让尾节点的next指向新增的节点
queueBottom.next = nodeQueue;
//以新节点作为尾节点
queueBottom = nodeQueue;
}
size ++;
}

/**
* 出队
* @return
*/
public Object removeNodeQueue(){
if(size == 0){
throw new RuntimeException("queue is null");
}
NodeQueue nodeQueue = queueTop;
queueTop = queueTop.next;
//声明原队列头next可以回收空间(GC)
nodeQueue.next = null;
size--;
return nodeQueue.data;
}


@Override
public String toString() {
StringBuilder sb = new StringBuilder("{ ");
for(NodeQueue nodeQueue = queueTop ; nodeQueue != null ; nodeQueue = nodeQueue.next){
sb.append(nodeQueue.data.toString()+" ");
}
return sb.toString()+"}";
}

public static void main(String[] args) {
Queue queue = new Queue();
queue.addNodeQueue("123");
queue.addNodeQueue("abc");
queue.addNodeQueue("ddd");
System.out.println(queue);
queue.removeNodeQueue();
System.out.println(queue);
queue.removeNodeQueue();
queue.removeNodeQueue();
System.out.println(queue);
}
}
文章目录
  1. 1. 队列介绍
  2. 2. 队列类型
  3. 3. 队列应用
  4. 4. java 实现
|