目录
  1. 1. 一、简介
  2. 2. 二、代码
    1. 2.1. 1 接口
    2. 2.2. 2 ArrayQueue(动态数组实现)
    3. 2.3. 3 LoopQueue循环队列(循环数组实现)
    4. 2.4. 4 LinkedListQueue
    5. 2.5. 5 对比
队列

一、简介

  • First in first out

二、代码

1 接口

1
2
3
4
5
6
7
public interface Queue<E> {
int getSize();
boolean isEmpty();
void enqueue(E e//入队,添加元素
E dequeue();
E getFront();//获得队首元素
}

2 ArrayQueue(动态数组实现)

  • 数组首端是队首,入队从队尾,出队从队首
  • 时间复杂度分析
    • 只有出队是0n 因为每次数组首元素出队,所有元素都要前移
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
public class ArrayQueue<E> implements Queue<E> {

private Array<E> array;

public ArrayQueue(int capacity){
array = new Array<>(capacity);
}

public ArrayQueue(){
array = new Array<>();
}

@Override
public int getSize(){
return array.getSize();
}

@Override
public boolean isEmpty(){
return array.isEmpty();
}

public int getCapacity(){
return array.getCapacity();
}

@Override
public void enqueue(E e){
array.addLast(e);
}

@Override
public E dequeue(){
return array.removeFirst();
}

@Override
public E getFront(){
return array.getFirst();
}

@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append("Queue: ");
res.append("front [");
for(int i = 0 ; i < array.getSize() ; i ++){
res.append(array.get(i));
if(i != array.getSize() - 1)
res.append(", ");
}
res.append("] tail");
return res.toString();
}

public static void main(String[] args) {

ArrayQueue<Integer> queue = new ArrayQueue<>();
for(int i = 0 ; i < 10 ; i ++){
queue.enqueue(i);
System.out.println(queue);
if(i % 3 == 2){
queue.dequeue();
System.out.println(queue);
}
}
}
}
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
147
148
149
150
151
152
153
154
155
public class Array<E> {

private E[] data;
private int size;

// 构造函数,传入数组的容量capacity构造Array
public Array(int capacity){
data = (E[])new Object[capacity];
size = 0;
}

// 无参数的构造函数,默认数组的容量capacity=10
public Array(){
this(10);
}

// 获取数组的容量
public int getCapacity(){
return data.length;
}

// 获取数组中的元素个数
public int getSize(){
return size;
}

// 返回数组是否为空
public boolean isEmpty(){
return size == 0;
}

// 在index索引的位置插入一个新元素e
public void add(int index, E e){

if(index < 0 || index > size)
throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");

if(size == data.length)
resize(2 * data.length);

for(int i = size - 1; i >= index ; i --)
data[i + 1] = data[i];

data[index] = e;

size ++;
}

// 向所有元素后添加一个新元素
public void addLast(E e){
add(size, e);
}

// 在所有元素前添加一个新元素
public void addFirst(E e){
add(0, e);
}

// 获取index索引位置的元素
public E get(int index){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Get failed. Index is illegal.");
return data[index];
}

public E getLast(){
return get(size - 1);
}

public E getFirst(){
return get(0);
}

// 修改index索引位置的元素为e
public void set(int index, E e){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Set failed. Index is illegal.");
data[index] = e;
}

// 查找数组中是否有元素e
public boolean contains(E e){
for(int i = 0 ; i < size ; i ++){
if(data[i].equals(e))
return true;
}
return false;
}

// 查找数组中元素e所在的索引,如果不存在元素e,则返回-1
public int find(E e){
for(int i = 0 ; i < size ; i ++){
if(data[i].equals(e))
return i;
}
return -1;
}

// 从数组中删除index位置的元素, 返回删除的元素
public E remove(int index){
if(index < 0 || index >= size)
throw new IllegalArgumentException("Remove failed. Index is illegal.");

E ret = data[index];
for(int i = index + 1 ; i < size ; i ++)
data[i - 1] = data[i];
size --;
data[size] = null; // loitering objects != memory leak

if(size == data.length / 4 && data.length / 2 != 0)
resize(data.length / 2);
return ret;
}

// 从数组中删除第一个元素, 返回删除的元素
public E removeFirst(){
return remove(0);
}

// 从数组中删除最后一个元素, 返回删除的元素
public E removeLast(){
return remove(size - 1);
}

// 从数组中删除元素e
public void removeElement(E e){
int index = find(e);
if(index != -1)
remove(index);
}

@Override
public String toString(){

StringBuilder res = new StringBuilder();
res.append(String.format("Array: size = %d , capacity = %d\n", size, data.length));
res.append('[');
for(int i = 0 ; i < size ; i ++){
res.append(data[i]);
if(i != size - 1)
res.append(", ");
}
res.append(']');
return res.toString();
}

// 将数组空间的容量变成newCapacity大小
private void resize(int newCapacity){

E[] newData = (E[])new Object[newCapacity];
for(int i = 0 ; i < size ; i ++)
newData[i] = data[i];
data = newData;
}
}

3 LoopQueue循环队列(循环数组实现)

image-20200313103316334

  • 主要是改进出队,不再将所有元素前移,而是将队首的指针front后移
  • 当前索引+1再百分之数组长度 是tail指针位置。
  • tail和front位置不能重复,重复就是空数组了,所以有意识的浪费一个空间,当前索引+2百分之数组长度 = front为满,此时应该触发扩容
  • 时间复杂度都是O1(入队出队因为有缩容所以使用均摊时间复杂度)
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
public class LoopQueue<E> implements Queue<E> {

private E[] data;
private int front, tail;
private int size; // 有兴趣的同学,在完成这一章后,可以思考一下:
// LoopQueue中不声明size,如何完成所有的逻辑?
// 这个问题可能会比大家想象的要难一点点:)

public LoopQueue(int capacity){
data = (E[])new Object[capacity + 1];//有意识的浪费一个空间
front = 0;
tail = 0;
size = 0;
}

public LoopQueue(){
this(10);
}

public int getCapacity(){
return data.length - 1;//有意识的浪费一个空间
}

@Override
public boolean isEmpty(){
return front == tail;
}

@Override
public int getSize(){
return size;
}

@Override
public void enqueue(E e){

if((tail + 1) % data.length == front)
resize(getCapacity() * 2);

data[tail] = e;
tail = (tail + 1) % data.length;
size ++;
}

@Override
public E dequeue(){

if(isEmpty())
throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
//返回出队的元素
E ret = data[front];
data[front] = null;
front = (front + 1) % data.length;
size --;
if(size == getCapacity() / 4 && getCapacity() / 2 != 0)
resize(getCapacity() / 2);
return ret;
}

@Override
public E getFront(){
if(isEmpty())
throw new IllegalArgumentException("Queue is empty.");
return data[front];
}

private void resize(int newCapacity){

E[] newData = (E[])new Object[newCapacity + 1];
for(int i = 0 ; i < size ; i ++)
//新数组从零开始,这样就要将原数组的偏移修正
newData[i] = data[(i + front) % data.length];

data = newData;
front = 0;
tail = size;
}

@Override
public String toString(){

StringBuilder res = new StringBuilder();
res.append(String.format("Queue: size = %d , capacity = %d\n", size, getCapacity()));
res.append("front [");
//另一种循环遍历方式
for(int i = front ; i != tail ; i = (i + 1) % data.length){
res.append(data[i]);
if((i + 1) % data.length != tail)
res.append(", ");
}
res.append("] tail");
return res.toString();
}

public static void main(String[] args){

LoopQueue<Integer> queue = new LoopQueue<>();
for(int i = 0 ; i < 10 ; i ++){
queue.enqueue(i);
System.out.println(queue);

if(i % 3 == 2){
queue.dequeue();
System.out.println(queue);
}
}
}
}

4 LinkedListQueue

  • 改进链表以适应队列,由于设置尾节点时可以找到尾结点下一个节点所以便于增加(入队操作),但不方便删除,因为要找尾结点上一个节点在单向链表中很麻烦,所以head端是队首,删除元素。

image-20200315103400476

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
public class LinkedListQueue<E> implements Queue<E> {

private class Node{
public E e;
public Node next;

public Node(E e, Node next){
this.e = e;
this.next = next;
}

public Node(E e){
this(e, null);
}

public Node(){
this(null, null);
}

@Override
public String toString(){
return e.toString();
}
}

private Node head, tail;
private int size;

public LinkedListQueue(){
head = null;
tail = null;
size = 0;
}

@Override
public int getSize(){
return size;
}

@Override
public boolean isEmpty(){
return size == 0;
}

@Override
public void enqueue(E e){
if(tail == null){
tail = new Node(e);
head = tail;
}
else{
tail.next = new Node(e);
tail = tail.next;
}
size ++;
}

@Override
public E dequeue(){
if(isEmpty())
throw new IllegalArgumentException("Cannot dequeue from an empty queue.");

Node retNode = head;
head = head.next;
retNode.next = null;
//注意只有一个节点的情况,出队为空
if(head == null)
tail = null;
size --;
return retNode.e;
}

@Override
public E getFront(){
if(isEmpty())
throw new IllegalArgumentException("Queue is empty.");
return head.e;
}

@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append("Queue: front ");

Node cur = head;
while(cur != null) {
res.append(cur + "->");
cur = cur.next;
}
res.append("NULL tail");
return res.toString();
}

public static void main(String[] args){

LinkedListQueue<Integer> queue = new LinkedListQueue<>();
for(int i = 0 ; i < 10 ; i ++){
queue.enqueue(i);
System.out.println(queue);

if(i % 3 == 2){
queue.dequeue();
System.out.println(queue);
}
}
}
}

5 对比

  • ArrayQueue复杂度是On^2^
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
mport java.util.Random;

public class Main {

// 测试使用q运行opCount个enqueueu和dequeue操作所需要的时间,单位:秒
private static double testQueue(Queue<Integer> q, int opCount){

long startTime = System.nanoTime();

Random random = new Random();
for(int i = 0 ; i < opCount ; i ++)
q.enqueue(random.nextInt(Integer.MAX_VALUE));
for(int i = 0 ; i < opCount ; i ++)
q.dequeue();

long endTime = System.nanoTime();

return (endTime - startTime) / 1000000000.0;
}

public static void main(String[] args) {

int opCount = 100000;

ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
double time1 = testQueue(arrayQueue, opCount);
System.out.println("ArrayQueue, time: " + time1 + " s");

LoopQueue<Integer> loopQueue = new LoopQueue<>();
double time2 = testQueue(loopQueue, opCount);
System.out.println("LoopQueue, time: " + time2 + " s");

LinkedListQueue<Integer> linkedListQueue = new LinkedListQueue<>();
double time3 = testQueue(linkedListQueue, opCount);
System.out.println("LinkedListQueue, time: " + time3 + " s");
}
}
文章作者: liuDH
文章链接: http://yoursite.com/2020/03/13/%E9%98%9F%E5%88%97/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 毛毛裤裤的世界
打赏
  • 微信
  • 支付寶