Abstract:For the queue, the data structure is more complex than the stack, but it is not very difficult to understand the first in first out, and then implement it with array or linked list.

This article is shared from Huawei cloud community《Handwritten all kinds of queue, a text is done》Author: bigsai.

## preface

Stack and queue are a pair of good brothers. The mechanism of stack is relatively simple. It’s like entering a narrow cave with only one entrance,**Only last in first out (those outside go out first, and those inside are a bit unlucky)**。 And the queue is like a tunnel, the people behind follow the front, the people in front go out first (first in first out). Daily queuing is a description of queue operation form!

Stack is a data structure that likes the new and dislikes the old. When the new comes, it will deal with the new and stop the old first (we hate this kind of people when we look for people or ask people to do things). Queue is a data structure that is selfless. Queue is first come first served and pays attention to the order. Therefore, this data structure is widely used in programming, middleware and so on, such as message queue, message queue, and so on FIFO disk scheduling, binary tree sequence traversal, BFS width first search and so on.

The core idea of the queue is: first in first out!

The concept of queue: queue is a special linear table, the special point is that it only allows delete operation in the front of the table, and insert operation in the back of the table. Like stack, queue is a linear table with limited operation. The end of inserting is called the tail of the team, and the end of deleting is called the head of the team.

## Queue introduction

We can choose a standard when we design the queue. Here we take the loop queue designed by Likou 622 as the standard of queue design.

**Front of the team:**Delete one end of the data.

**At the end of the team:**Insert one end of the data.

**For arrays,**It is easier to insert from the back of the array, but it is more difficult to insert from the front of the array; For the linked list, the insertion and deletion are carried out at the two ends respectively, so the head (front) deletion and tail insertion are the most convenient choices.

Implementation method:

- Mycircular queue (k): constructor, set the queue length to K.
- Front: get the element from the team leader. If the queue is empty, return – 1.
- Real: get the tail element. If the queue is empty, return – 1.
- Enqueue (value): inserts an element into the circular queue. True if inserted successfully.
- Dequeue(): removes an element from the circular queue. True if successfully deleted.
- Isempty(): check if the loop queue is empty.
- Isfull(): check if the loop queue is full.

### Normal queue

Following the introduction above, it’s easy to know how arrays are implemented. Array simulation is used to represent the queue. Consider initialization, insertion, and other issues.

In this ordinary queue, some operations should be noted as follows:

**initialization:**Both front and rear of the array point to 0. (when the subscripts of front and rear are equal, the queue is empty)

Join the team: the team is dissatisfied, the array does not cross the boundary, first pass the value at the end of the team, and then the subscript at the end of the team + 1 (the real at the end of the team is actually one bit ahead, in order to distinguish the empty queue)

Out of the team: if the team is not empty, take the position element of the team head first, and add 1 at the team head.

But it is easy to find the problem, each space domain can only be used once, resulting in extreme waste of space, very easy to cross the border!

### Circular queue (array implementation)

In view of the above problems. There is a better solution! It is to reuse the memory that has been applied. This is what we call circular queues. One of the advantages of circular queues is that we can use the space previously used by the queue. In a normal queue, once a queue is full, we cannot insert the next element, even if there is still space in front of the queue. But with circular queues, we can use this space to store new values.

Array implementation of the circular queue is to make logical changes. Because we only need front and rear pointers in the queue. Logically, the real is behind and the front is in front, but in fact, they are not necessarily in front or behind. When calculating the distance, you need to add the array length to the real, subtract the front, and then find the remainder.

**initialization:**The front and rear of the array point to 0. It should be noted that when the front and rear are in the same position, it is proved that the queue is empty. In addition, when I implement it, I make the array application larger and empty a position to prevent the front and rear from being in the same position when the queue is full.

**Team entry:**If the team is dissatisfied, first pass the value at the end of the team, then real = (real + 1)% maxsize;

**Out of the team:**If the team is not empty, first take the team head position element, front = (front + 1)% maxsize;

If you need to turn to the head position at the end, you can find the position directly by + 1 (which is more concise than judging whether it is at the end), where maxsize is the actual size of the array.

**Is it empty**return rear == front;

**size:**return (rear+maxsize-front)%maxsize; It’s easy to understand here. A diagram can explain clearly. Both front and back can meet the requirements.

Here are a few things you need to pay attention to, that is, the addition of indicators, if you need to turn to the head at the end. You can determine whether to reach the end of the array. You can also directly add 1 to find the remainder. Where maxsize is the actual size of the array.

Specific implementation:

```
public class MyCircularQueue {
private int data[];// Array container
private int front;// head
private int rear;// tail
private int maxsize;// Maximum length
public MyCircularQueue(int k) {
data = new int[k+1];
front = 0;
rear = 0;
maxsize = k+1;
}
public boolean enQueue(int value) {
if (isFull())
return false;
else {
data[rear] = value;
rear=(rear + 1) % maxsize;
}
return true;
}
public boolean deQueue() {
if (isEmpty())
return false;
else {
front=(front+1)%maxsize;
}
return true;
}
public int Front() {
if(isEmpty())
return -1;
return data[front];
}
public int Rear() {
if(isEmpty())
return -1;
return data[(rear-1+maxsize)%maxsize];
}
public boolean isEmpty() {
return rear == front;
}
public boolean isFull() {
return (rear + 1) % maxsize == front;
}
}
```

## Circular queue (implementation of linked list)

For the queue implemented by linked list, the position of head and tail should be considered according to the first in first out rule

We know that the queue is first in first out. For the linked list, we can use the single linked list. We can use the single linked list as much as possible, which is as convenient as possible, and at the same time, we should also consider the efficiency. There are two ways to use linked list

**Scheme 1:**If the queue head is set at the end of the chain list, the queue tail is set at the head of the chain. So the end of the team into the team, insert in the head of the list no problem, easy to achieve, but if the head of the team to delete in the tail of the list, if you do not set the tail pointer to traverse to the end of the team, but set the tail pointer to delete it, the precursor node needs two-way list, it is very troublesome.

**Scheme 2:**If the head of the queue is set at the head of the chain and the tail of the queue is set at the end of the list, then the tail of the queue can be inserted at the end of the list. It is easy to insert the tail of the list (you can point to next directly with the tail pointer). If the head of the queue is deleted at the head of the list, it is also easy to delete the head node.

So we finally adopt the single linked list with the leading node and tail pointer of scheme 2!

The main operations are as follows:

**initialization:**Set up a head node. Both front and rear point to it first.

**Team entry:**rear.next=va; rear=va;（ VA is the inserted node)

**Out of the team:**The team is not empty, front. Next = front. Next. Next; Classic leading node deletion, but if only one node is deleted, you need to add a real = front, otherwise the real will lose contact.

**Is it empty**return rear == front; Or user defined maintenance len judge return len = = 0

**size:**Node front traverses to the number of real, or custom maintenance len returns directly (not implemented here).

Implementation code:

```
public class MyCircularQueue{
class node {
int data;// Results for nodes
node next;// Next connected node
public node() {}
public node(int data) {
this.data = data;
}
}
node front;// Equivalent to the head node
node rear;// Equivalent to tail / end
int maxsize;// Maximum length
int len=0;
public MyCircularQueue(int k) {
front=new node(0);
rear=front;
maxsize=k;
len=0;
}
public boolean enQueue(int value) {
if (isFull())
return false;
else {
node va=new node(value);
rear.next=va;
rear=va;
len++;
}
return true;
}
public boolean deQueue() {
if (isEmpty())
return false;
else {
front.next=front.next.next;
len--;
//Note that if it is deleted, you need to point the real to the front
if(len==0)
rear=front;
}
return true;
}
public int Front() {
if(isEmpty())
return -1;
return front.next.data;
}
public int Rear() {
if(isEmpty())
return -1;
return rear.data;
}
public boolean isEmpty() {
return len==0;
//return rear == front;
}
public boolean isFull() {
return len==maxsize;
}
}
```

## Two way queue

Design and implementation of two terminal queue, in fact, you often use arraydeque is a classic two-way queue, which is based on the array implementation, the efficiency is very high. The bidirectional queue template we implement here is based on liku641 to design circular double ended queue.

Your implementation needs to support the following operations:

- Mycirculardeque (k): constructor, the size of the double ended queue is K.
- Insertfront(): adds an element to the head of the two-way queue. Returns true if the operation is successful.
- Insertlast(): adds an element to the end of the two ended queue. Returns true if the operation is successful.
- Deletefront(): removes an element from the head of a two-way queue. Returns true if the operation is successful.
- Deletelast(): removes an element from the end of a two-way queue. Returns true if the operation is successful.
- Getfront(): gets an element from the head of the two-way queue. If the double ended queue is empty, return – 1.
- Getreal(): gets the last element of the two terminal queue. If the double ended queue is empty, return – 1.
- Isempty(): check whether the two terminal queue is empty.
- Isfull(): check whether the double ended queue is full.

In fact, with the above foundation, it is very easy to implement a double ended queue. Many operations are consistent with the single ended circular queue. There is only one more operation to insert the queue head and delete the queue tail

**Team leader insert:**The subscript position of team mate’s front itself has a value, so we need to back the front by one bit and then assign a value, but we need to consider whether it is full or the array is out of bounds.

**Delete at the end of the team:**We only need to subtract 1 from the real position, and we also need to consider whether it is empty or out of bounds.

Specific implementation code:

```
public class MyCircularDeque {
private int data[];// Array container
private int front;// head
private int rear;// tail
private int maxsize;// Maximum length
/*The maximum initialization size is K*/
public MyCircularDeque(int k) {
data = new int[k+1];
front = 0;
rear = 0;
maxsize = k+1;
}
/**Head insertion*/
public boolean insertFront(int value) {
if(isFull())
return false;
else {
front=(front+maxsize-1)%maxsize;
data[front]=value;
}
return true;
}
/**Tail insertion*/
public boolean insertLast(int value) {
if(isFull())
return false;
else{
data[rear]=value;
rear=(rear+1)%maxsize;
}
return true;
}
/**Normal head deletion*/
public boolean deleteFront() {
if (isEmpty())
return false;
else {
front=(front+1)%maxsize;
}
return true;
}
/**Tail deletion*/
public boolean deleteLast() {
if(isEmpty())
return false;
else {
rear=(rear+maxsize-1)%maxsize;
}
return true;
}
/** Get the front item */
public int getFront() {
if(isEmpty())
return -1;
return data[front];
}
/** Get the last item from the deque. */
public int getRear() {
if(isEmpty())
return -1;
return data[(rear-1+maxsize)%maxsize];
}
/** Checks whether the circular deque is empty or not. */
public boolean isEmpty() {
return front==rear;
}
/** Checks whether the circular deque is full or not. */
public boolean isFull() {
return (rear+1)%maxsize==front;
}
}
```

## summary

For the queue, the data structure is more complex than the stack, but it is not very difficult to understand the first in first out, and then implement it with array or linked list.

For arrays, the position of tail at the end of the queue is empty, while the front (head) of the linked list is empty, and the pointer is empty. Therefore, we need to pay attention to the methods to achieve the same effect in different structures.

The circular queue implemented by array can make great use of array space, while the bidirectional queue is an efficient data structure that can be used as both a queue and a stack, so it is necessary to master it.

**Click follow to learn about Huawei’s new cloud technology for the first time~**