1、队列的定义及基本概念
“队列”(Queue)这个单词时英国人说的 “排”(line)(一种等待服务的方式),在生活中,队列在我们日常生活中经常碰到,例如,排队买东西。
在计算机科学中,队列是一种数据结构,是一种特殊的线性表。和栈类似。但相差很大。
队的操作是在两端进行,其中一端只能进行插入,该端称为队列的队尾,而另一端只能进行删除,该端称为队列的队首。队列的运算规则时FIFO(First In First Out)

队列的基本运算:
- 置空队:建立一个空队列 
- 判断队空:队列是否为空 
- 判断队满:队列是否满 
- 入队:在队列非满时,从队尾插入元素 
- 出队:在队列非空时,从队首删除元素 
- 取队首元素:不删除元素,返回队首元素 
- 遍历队列:打印出数组中的有效数据 
队列的存储具有顺序存储和链式存储两种。顺序存储通过数组存储,链式存储通过链表存储。
2、单向队列
我们通过数组来模拟单向队列。
由于队列的对头和队尾的位置是变化的,因而要设置两个指针front和rear来分别指示队头元素和队尾元素在空间中的位置。
在构造顺序队列时,两个指针初始化置为0,入队时将新元素插入rear所指的位置,然后将rear加1,出队时,删除front所指的元素,然后将front加1并返回被删元素。
在入队和出队时,我们需要判断队空和队满。
图解:
初始化:创建一个maxSize的数组  。  front 和 rear 为-1 。

添加数据:先将rear加1,然后将rear指向的数组赋值

删除数据:先front加1,然后取出front指向的数据,因为front指向的是队列头的前一个位置。

判断为满:可以看出。当rear 等于 maxSize-1时,队列满。

判断为空:当font和rear相等时,为空

| 12
 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
 
 | class ArrayQueue{
 private int maxSize;
 
 private int front;
 
 private int rear;
 
 private int[] arr;
 
 
 public ArrayQueue(int arrMaxSize){
 maxSize = arrMaxSize;
 
 arr = new int[maxSize];
 
 
 front = -1;
 
 rear = -1;
 }
 
 
 public boolean isFull(){
 return rear == maxSize - 1;
 }
 
 
 public boolean iSEmpty(){
 return front == rear;
 }
 
 
 public void AddQueue(int n){
 if(!isFull()){
 rear++;
 arr[rear] = n;
 }
 else {
 System.out.println("队列已满,无法添加数据");
 }
 }
 
 
 public int GetQueue(){
 if(iSEmpty()){
 System.out.println("队列为空,无法返回数据");
 throw new RuntimeException("队列空,不能取数据");
 }
 
 front++;
 return arr[front];
 }
 
 
 public void ShowQueue(){
 if(iSEmpty()){
 System.out.println("队列为空");
 return;
 }
 
 for (int i = front; i < rear; i++) {
 System.out.print(arr[i + 1] + "\t");
 }
 
 System.out.println();
 }
 
 
 public int GethandQueue(){
 if(iSEmpty()){
 System.out.println("队列为空,无法返回数据");
 throw new RuntimeException("队列空,不能取数据");
 }
 
 return arr[front + 1];
 }
 
 }
 
 | 
测试:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | public static void main(String[] args) {ArrayQueue arrayQueue = new ArrayQueue(3);
 System.out.println(arrayQueue.iSEmpty());
 
 arrayQueue.AddQueue(1);
 arrayQueue.AddQueue(2);
 arrayQueue.ShowQueue();
 
 arrayQueue.GetQueue();
 arrayQueue.ShowQueue();
 
 System.out.println(arrayQueue.GethandQueue());
 
 }
 
 | 
问题:
我们添加三次数据,然后删除3次数据。 显示为满,但实际上队列中没有数据
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | public static void main(String[] args) {ArrayQueue arrayQueue = new ArrayQueue(3);
 
 arrayQueue.AddQueue(1);
 arrayQueue.AddQueue(2);
 arrayQueue.AddQueue(3);
 
 arrayQueue.GetQueue();
 arrayQueue.GetQueue();
 arrayQueue.GetQueue();
 
 System.out.println(arrayQueue.isFull());
 }
 
 | 
此时数组为空,但无法存储数据;

为了解决这个问题,我们引入循环队列,消除假溢出;
3、循环队列
我们通过取模来实现循环队列,将数组看成一个环形。也就是当队尾到最大值maxSize时,取模,使队尾等于0。
在循环队列中,判空的条件使rear == front ,判满的条件也是rear == front,为了避免二义性。所以人为的浪费一个空间,这样判满的条件为 front == (rear + 1)% maxSize;
图解:
初始化:front和rear都等于0。
此时的front和rear的含义与单向队列中的含义不同了;
front是指向队头
rear是指向队尾的下一个数据

添加一个数据:先存入数据,然后:rear  = (rear + 1)% maxSize

删除一个数据: 因为front指向的是当前数据,只能用一个临时变量将当前的数据存上,然后 front = (front + 1)% maxSize,最后返回临					   时变量

判空:rear == front

判满:front == (rear + 1)% maxSize

| 12
 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
 
 | class ArrayCircularQueue{
 
 private int maxSize;
 
 
 private int front;
 
 
 private int rear;
 
 
 private int[] arr;
 
 
 public ArrayCircularQueue(int Size){
 maxSize = Size;
 
 front = 0;
 rear = 0;
 
 arr  = new int[maxSize];
 }
 
 
 public boolean isEmpty(){
 return rear == front;
 }
 
 
 public boolean isFull(){
 return front == (rear + 1) % maxSize;
 }
 
 
 public void AddQueue(int n){
 if (isFull()){
 System.out.println("队列已满");
 }
 
 arr[rear] = n;
 rear = (rear + 1) % maxSize;
 
 }
 
 
 public int GetQueue(){
 if(isEmpty()){
 System.out.println("队列为空,无法返回数据");
 throw new RuntimeException("队列空,不能取数据");
 }
 
 int value = arr[front];
 front = (front + 1) % maxSize;
 
 return value;
 }
 
 
 public void ShowQueue(){
 if (isEmpty()){
 System.out.println("队列为空");
 return;
 }
 
 int i = front;
 
 while(i != rear)
 {
 System.out.print(arr[i] + "\t");
 
 i = (i + 1) % maxSize;
 }
 System.out.println();
 }
 
 
 public int Size(){
 return (rear - front + maxSize) % maxSize;
 }
 
 
 public int getHandQueue(){
 if (isEmpty()){
 System.out.println("队列为空,无法返回数据");
 throw new RuntimeException("队列空,不能取数据");
 }
 
 return arr[front];
 }
 
 }
 
 | 
测试:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | public static void main(String[] args) {
 ArrayCircularQueue arrayCircularQueue = new ArrayCircularQueue(3);
 
 System.out.println(arrayCircularQueue.isEmpty());
 
 arrayCircularQueue.AddQueue(1);
 arrayCircularQueue.AddQueue(2);
 
 arrayCircularQueue.ShowQueue();
 
 arrayCircularQueue.GetQueue();
 
 arrayCircularQueue.ShowQueue();
 
 System.out.println(arrayCircularQueue.getHandQueue());
 
 System.out.println(arrayCircularQueue.Size());
 
 
 }
 
 | 
这样就解决了假溢出问题,队列也可以重复使用:
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | ArrayCircularQueue arrayCircularQueue = new ArrayCircularQueue(3);
 arrayCircularQueue.AddQueue(1);
 arrayCircularQueue.AddQueue(2);
 
 arrayCircularQueue.GetQueue();
 arrayCircularQueue.GetQueue();
 
 System.out.println(arrayCircularQueue.isFull());
 
 |