This article takes you to understand the queue data structure

Time:2021-7-21

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.
This article takes you to understand the queue data structure

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.
This article takes you to understand the queue data structure

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.
This article takes you to understand the queue data structure

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!
This article takes you to understand the queue data structure

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.
This article takes you to understand the queue data structure

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 emptyreturn 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.
This article takes you to understand the queue data structure

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)
This article takes you to understand the queue data structure

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.
This article takes you to understand the queue data structure

Is it emptyreturn 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~

Recommended Today

A detailed explanation of the differences between Perl and strawberry Perl and ActivePerl

Perl is the abbreviation of practical extraction and report language “practical report extraction language”. Application of activestateperl and strawberry PERL on Windows platformcompiler。 Perl   The relationship between the latter two is that C language and Linux system have their own GCC. The biggest difference between activestate Perl and strawberry Perl is that strawberry Perl […]