The interviewer asked me to write the queue by hand. I almost didn’t write it

Time:2022-5-14

preface

Stack and queue are a pair of good brothers. We introduced an article on stack earlier (stack, not LIFO). The mechanism of stack is relatively simple. LIFO is like entering a narrow cave. The cave has only one entrance and exit and can onlyLast in first out。 The queue is like a tunnel, and the people behind follow the front,People in front go out first (first in, first out)。 Daily queuing is a description of the form of queue operation!

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

The core idea of queue is:fifo!

Concept of queue: queue is a special linear table. The special feature is that it only allows deletion at the front of the table and insertion at the back of the table. Like stack, queue is a linear table with limited operation. The end that performs the insertion operation is called the tail of the queue, and the end that performs the deletion operation is called the head of the queue.

At the same time, you’d better understand this article firstBasic operation of sequence tableandData structure of stack! Better learning effect!
The interviewer asked me to write the queue by hand. I almost didn't write it

Queue introduction

We can choose a standard when designing the queue. Here we take itForce buckle 622 design cycle queueAs the standard of queue design.

Team leader front:Delete one end of the data.

Rear:Insert one end of the data.

For arrays, it is easier to insert from the back of the array, and it is more difficult to insert from the front of the array, so the queue head is in front of the array and the queue tail is behind the array; And forLinked list, the insertion and deletion are carried out at two ends respectively, so the head (front) deletion and tail insertion are the most convenient choice.

The interviewer asked me to write the queue by hand. I almost didn't write it

Implementation method:

  • Mycircularqueue (k): constructor that sets the queue length to K.
  • Front: get the element from the team leader. If the queue is empty, – 1 is returned.
  • Rear: get the tail element. If the queue is empty, – 1 is returned.
  • Enqueue (value): inserts an element into the circular queue. Returns true if successfully inserted.
  • Dequeue(): deletes an element from the circular queue. Returns true if the deletion was successful.
  • Isempty(): check whether the circular queue is empty.
  • Isfull(): check whether the circular queue is full.

Ordinary queue

According to the above introduction, it is easy to know the implementation of array. The queue is represented by an array simulation. Consider initialization, insertion, and other issues.
The interviewer asked me to write the queue by hand. I almost didn't write it

In this ordinary queue, some operations need to be noted:

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: if the team is dissatisfied and the array does not cross the boundary, pass the value at the end of the team first, and then the subscript at the end of the team + 1 (the end of the team rear 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’s easy to findproblem, each space domain can only be used once, resulting in an extreme waste of space and easy to cross the boundary!
The interviewer asked me to write the queue by hand. I almost didn't write it

Circular queue (array implementation)

In view of the above problems. There is a better solution! That’s rightAlready appliedMemory reuse of (array). This is what we call a circular queue. One of the benefits of circular queues is that we can use the space previously used by this 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.

The circular queue implemented by array islogicallyMake changes. Because we only need front and rear pointers in the queue. Logically, rear is in the back and front is in the front, but in fact, they are not necessarily who is in the front and who is in the back. When calculating the distance, you need to add the array length of rear, subtract front, and then calculate the remainder.

The interviewer asked me to write the queue by hand. I almost didn't write it

initialization: both front and rear of the array point to 0 It should be noted here that when front and rear are in the same position, it proves that the queue is empty. In addition, during the specific implementation, I will empty a large position of the array application to prevent the front and rear from being in the same position when the queue is full.

Join the teamIf the team is dissatisfied, first pass the value at the end of the team, and then:rear=(rear + 1) % maxsize;

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

Here, the indexes of out and in team are added. If you need to turn to the head position at the end, you can directly + 1 to find the position (which is more concise than judging whether it is at the end), wheremaxsizeIs the actual size of the array.

Is it emptyreturn rear == front;

sizereturn (rear+maxsize-front)%maxsize;It’s easy to understand here. A picture can explain it clearly. Both front and rear can meet the requirements.

The interviewer asked me to write the queue by hand. I almost didn't write it

There are several things we need to pay attention to, that is, the addition of indicators. If we need to turn to the head in the end. You can determine whether to reach the end of the array. You can also find the remainder directly by + 1. amongmaxsizeIs 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 (linked list implementation)

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 and try to use the single linked list, which is as convenient as possible, while taking into account efficiency. There are probably two implementation schemes for using linked lists:

Scheme IIf the queue head is set at the end of the chain list, the end of the queue is set at the head of the chain. thatTeam tail in team insertionThere is no problem inserting in the head of the linked list, which is easy to implement, but ifTeam leader deletionAt the end of the linked list, if you do not set the tail pointer, you need to traverse to the end of the queue, but if you set the tail pointer, you need to delete its predecessor nodeTwo way linked list is required, it’s all troublesome.

Scheme IIIf the queue head is set at the chain header and the queue tail is set at the end of the chain list, thenTeam tail in team insertionThere is no problem inserting at the end of the linked list (the tail pointer can directly point to next), which is easy to implement. IfTeam leader deletionIt’s also easy to do it at the head of the linked list, which is what we often said earlierHead node delete node

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

The main operations are:

initialization: set up a header node. Both front and rear point to it first.

Join the teamrear.next=va;rear=va; (VA is the inserted node)
The interviewer asked me to write the queue by hand. I almost didn't write it

Out of the team: the team is not empty,front.next=front.next.next;The classic lead node is deleted, but if only one node is deleted, you need to add an additional rear = front, otherwise the rear will be lost.
The interviewer asked me to write the queue by hand. I almost didn't write it

Is it emptyreturn rear == front;Or customize maintenance len judgmentreturn len==0

size: the number of nodes traversed from front to rear, or the user-defined maintenance len is returned directly (not implemented here).

Implementation code:

public class MyCircularQueue{
     class node {
        int data;//  Result of node
        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 rear 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 (extra meal)

In fact, the arraydeque you often use is a classic two-way queue, which is implemented based on array and is very efficient. The bidirectional queue template we implement here is based onForce buckle 641 design cycle double ended queue
Your implementation needs to support the following operations:

  • Mycirculardeque (k): constructor. The size of double ended queue is K.
  • Insertfront(): adds an element to the head of the double ended queue. Returns true if the operation is successful.
  • Insertlast(): adds an element to the end of the double ended queue. Returns true if the operation is successful.
  • Deletefront(): deletes an element from the head of the double ended queue. Returns true if the operation is successful.
  • Deletelast(): deletes an element from the end of the double ended queue. Returns true if the operation is successful.
  • Getfront(): get an element from the head of the double ended queue. If the double ended queue is empty, – 1 is returned.
  • Getrear (): get the last element of the double ended queue. If the double ended queue is empty, – 1 is returned.
  • Isempty(): check whether the double ended 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. There are many operations consistent with the single ended circular queue, only one moreTeam head insertionandEnd of line deletionThe two operations are briefly analyzed:

Team head insertion: teammate: the subscript position of the front itself has a value, so it is necessary to step back the front one bit and then assign a value, but it is necessary to consider whether it is full or the array is out of bounds.

End of line deletion: you only need to subtract 1 from the rear position, but also consider whether it is empty and 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. It can be realized by first in first out and then array or linked list.

For arrays, the position pointed to by the tail at the end of the queue is empty, while the front (like the head) of the linked list is the head, and the pointer is empty. Therefore, attention should be paid to the methods to achieve the same effect in different structures.

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

Finally, the author’s level is limited, if there are mistakes and deficienciesPlease also indicate。 In addition, if you feel good, you can praise and pay attentionPersonal official accountbigsaiMore often share with you, pay attention to the replybigsaiGet a carefully prepared learning material!

Recommended Today

Extcon driver and its application in USB driver

Extcon, short for external connector, is used to abstract external connectors, such as audio jack, USB microb / typec interface, etc. Its prototype is the switch class driver of Android. After modification, it was introduced into the kernel in kernel version 3.4.0. Extcon (external connector): import Android’s switch class and modify. External connector class (extcon) is based on and an extension of Android kernel’s switch class located at linux/drivers/switch/. https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/drivers/extcon?h=next-20220502&id=de55d8716ac50a356cea736c29bb7db5ac3d0190 The main function of extcon driver is to identify the […]