[C language brush question] Leetcode622 - design circular queue

Event address: CSDN 21-day learning challenge

Leetcode622 - Design Circular Queue

topic description

Design your circular queue implementation. A circular queue is a linear data structure whose operation is based on the FIFO (first in first out) principle and the tail of the queue is connected after the head of the queue to form a loop. It is also known as a "ring buffer".
One benefit of circular queues is that we can utilize previously used space on this queue. In a normal queue, once a queue is full, we cannot insert the next element, even if there is still room at the front of the queue. But with a circular queue, we can use this space to store new values.

Link : https://leetcode.cn/problems/design-circular-queue

Your implementation should support the following operations:

MyCircularQueue(k): Constructor, set the queue length to k.
Front: Get elements from the front of the queue. Returns -1 if the queue is empty.
Rear: Get the element at the end of the queue. Returns -1 if the queue is empty.
enQueue(value): Insert an element into the circular queue. Returns true if the insertion was successful.
deQueue(): Remove an element from the circular queue. Returns true if the deletion was successful.
isEmpty(): Checks if the circular queue is empty.
isFull(): Checks if the circular queue is full.

Example :

MyCircularQueue circularQueue = new MyCircularQueue(3); // Set the length to 3
circularQueue.enQueue(1); // Return true
circularQueue.enQueue(2); // Return true
circularQueue.enQueue(3); // Return true
circularQueue. enQueue(4); // returns false, the queue is full
circularQueue.Rear(); // returns 3
circularQueue.isFull(); // returns true
circularQueue.deQueue(); // returns true
circularQueue.enQueue(4); // returns true
circularQueue.Rear(); // returns 4


Tips :

  • All values ​​are in the range 0 to 1000;
  • Operands will be in the range 1 to 1000;
  • Please do not use the built-in queue library.

core code pattern

typedef struct {
    
    

} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    
    

}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    
    

}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    
    

}

int myCircularQueueFront(MyCircularQueue* obj) {
    
    

}

int myCircularQueueRear(MyCircularQueue* obj) {
    
    

}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    
    

}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    
    

}

void myCircularQueueFree(MyCircularQueue* obj) {
    
    

}

/**
 * Your MyCircularQueue struct will be instantiated and called as such:
 * MyCircularQueue* obj = myCircularQueueCreate(k);
 * bool param_1 = myCircularQueueEnQueue(obj, value);
 
 * bool param_2 = myCircularQueueDeQueue(obj);
 
 * int param_3 = myCircularQueueFront(obj);
 
 * int param_4 = myCircularQueueRear(obj);
 
 * bool param_5 = myCircularQueueIsEmpty(obj);
 
 * bool param_6 = myCircularQueueIsFull(obj);
 
 * myCircularQueueFree(obj);
*/

Idea analysis and code implementation (C language)

It is not very good to use a single-linked list. For example, if you want to get the elements at the end of the queue, you have to traverse it again, but you can directly use the subscript -1 at the end of the queue with the sequence table. In addition, the creation of a single-linked list is more troublesome than the sequence table, and you need to use them one by one. Nodes are created and all are linked. It is better to use the dynamic sequence table.
The insertion or deletion of elements in the circular queue does not change the space, and the strategy of overwriting is adopted. In fact, the key to this question is to accurately determine whether it is empty or full. Here we let the rear point to the next position of the element at the end of the queue, and then the problem arises. We cannot distinguish the two states of empty and full. Why?
As shown in the figure, both the queue head pointer and the queue tail pointer point to the same position in both empty and full states.
image.png

Solution :

  1. Increment a size to count
  2. Add a space, and always leave a place when it is full. For example, if the circular queue has 4 elements, 5 spaces will be opened.

The next position of the rear position is the front, which is the full state, and the front and rear point to the same position, which is the empty state. However, it should be noted that this is the conclusion of the logical structure. As shown in the figure, the queue of the logical structure is full, but we use a dynamic sequence table to realize the circular queue, but the physical structure is another form. Our operations must be based on physics. structure.
image.png
As shown in the picture, we should pay attention to how to deal with it, let the rear subscript + 1 %N, N represents the total number of spaces (including an extra space), and judge whether the result is equal to the front subscript.
image.png
Structure declaration and queue creation
The circular list is implemented using a dynamic sequence table, setting the subscript of the head and tail of the queue, and an N representing the total number of spaces opened by the dynamic sequence table (one more).
Create a queue to apply for space on the heap, as does the dynamic sequence table, open a space more than the given k, and return the queue pointer after initialization is completed.

typedef struct {
    
    
    int* arr;
    int front;
    int rear;
    int N;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    
    
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->arr = (int*)malloc((k+1)*sizeof(int));
    obj->front = obj->rear = 0;
    obj->N = k + 1;
    return obj;
}

If the front
and rear are equal, it means that the queue is empty, and if the judgment is full, there are two situations that should be paid attention to, which are mentioned above.

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    
    
    return obj->rear == obj->front;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    
    
    return ((obj->rear+1) % obj->N) == obj->front;
}

If the element queue
is full, it will not be allowed to be put in, and false will be returned. If it is not full, the element will be placed directly in the rear position. Note that you must remember to wait for obj->N. This is to control that if the rear reaches the end of the space, it can return The position of subscript 0, so as to realize the loop.

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    
    
    if(myCircularQueueIsFull(obj))
        return false;
    obj->arr[obj->rear] = value;
    ++obj->rear;
    obj->rear %= obj->N;
    return true;
}

If the element is out of the queue
, it will fail to be out of the queue and return false. If it is not empty, the front will be moved forward one bit. Note that you should remember to wait for obj->N. This is to control that if the rear reaches the end of the space, it can return to The position of subscript 0, so as to realize the loop.

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    
    
    if(myCircularQueueIsEmpty(obj))
        return false;
    ++obj->front;
    obj->front %= obj->N;
    return true;
}

Get the queue head element
first to see if the queue is empty, if it is empty, return -1, if not empty, just return the queue head element directly.

int myCircularQueueFront(MyCircularQueue* obj) {
    
    
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->arr[obj->front];
}

To get the elements at the end of the queue,
first check whether the queue is empty. If it is empty, return -1. If it is not empty, there are two situations to pay attention to: rear is 0 and rear is not 0.
There are two ways of writing:

int myCircularQueueRear(MyCircularQueue* obj) {
    
    
    if(myCircularQueueIsEmpty(obj))
        return -1;
    else if(obj->rear == 0)
        return obj->arr[obj->N - 1];
    else
        return obj->arr[obj->rear - 1];
}


There are two structures that are dynamically created by destroying and releasing the queue , one is a queue structure, and the other is a dynamic array. The array must be released first and then the structure.

void myCircularQueueFree(MyCircularQueue* obj) {
    
    
    free(obj->arr);
    obj->arr = NULL;
    obj->front = obj->rear = obj->N = 0;
    free(obj);
}

insert image description here

Guess you like

Origin blog.csdn.net/weixin_61561736/article/details/126418702