Data Structure: Implementation of Circular Queue (leetcode622. Design Circular Queue)

 

Table of contents

1. A brief introduction to circular queues

2. Implement circular queue with static array

1. Array circular queue structure design

2. The heap area memory application interface of the array circular queue 

3. Interface realization of data dequeue and enqueue

4. Other operation interfaces

5. Overview of the implementation code of the array circular queue 

3. Static one-way circular linked list realizes circular queue 

1. Structure design of linked list circular queue

2. Create an interface for a static one-way circular linked list

3. Data dequeue and enqueue interface

4. Other queue operation interfaces

5. Static linked list circular queue overall code


Problem source: 622. Design Circular Queue - Leetcode

1. A brief introduction to circular queues

  • A circular queue is generally a static linear data structure in which the data conforms to the first-in-first-out principle .
  • The container head address and container tail address of the circular queue are connected through specific operations (such as pointer linking, array subscripting , etc. ), thus realizing the reuse of container space ( in an acyclic static queue , once a queue is full , we cannot insert the next element, even though there is still space at the front of the queue )

 

2. Implement circular queue with static array

Maintain the structure of the queue:

typedef struct 
{
    int * arry;  //指向堆区数组的指针
    int head;    //队头指针
    int tail;    //队尾指针(指向队尾数据的下一个位置)(不指向有效数据)
    int capacity;//静态队列的容量
} MyCircularQueue;

1. Array circular queue structure design

We assume that the capacity of the static array is k ( can store k data ) :

  • According to the basic data structure of the queue : there are two pointers used to maintain the effective data space in the array , namely the head pointer and the tail pointer, the head pointer is used to point to the data at the head of the queue , and the tail is used to point to the next position of the data at the tail of the queue ( That is, the tail pointer does not point to valid data)
  •  As shown in the figure, the memory space for valid data is between the head pointer and the tail pointer
  • The relationship between the head pointer and the tail pointer is used to realize the judgment of fullness of the queue ( judging whether the queue space is full ) and judgment of emptyness ( judging whether the queue is empty ); in order to achieve this goal, we need to set the capacity of the static array For k+1 (i.e. set one more element space) 
  1. Queue empty condition: tail == head;
  2. Queue full condition: (tail+1)%(k+1) == head;  Another situation:
  • From this, we can first design the full and empty interface of the queue :
    bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判断队列是否为空
    {
        assert(obj);
        return (obj->tail == obj->head);
    }
    
    bool myCircularQueueIsFull(MyCircularQueue* obj)  //判断队列是否为满
    {
        assert(obj);
        return ((obj->tail+1)%(obj->capacity +1) == obj->head);
    }

2. The heap area memory application interface of the array circular queue 

  • Create a MyCircularQueue structure on the heap , and apply for an array of (k+1)*sizeof(DataType) bytes for the queue at the same time :
    MyCircularQueue* myCircularQueueCreate(int k)  //k个容量大小的循环队列的初始化接口
    {
        MyCircularQueue * tem = (MyCircularQueue *)malloc(sizeof(MyCircularQueue));
        //开辟维护循环队列的结构体
        if(NULL == tem)
        {
            perror("malloc failed");
            exit(-1);
        }
        tem->arry = NULL;
        tem->capacity = k;   
        //队列的数据容量为k
        tem->arry = (int*)malloc((k+1)*sizeof(int));
        //开辟堆区数组
        if(NULL == tem->arry)
        {
            perror("malloc failed");
            exit(-1);
        }
        //将head,tail下标初始化为0
        tem->head = 0; 
        tem->tail = 0;
        return tem;
    }

3. Interface realization of data dequeue and enqueue

Diagram of data dequeue and enqueue:

 

  •  According to the diagram, we can design the interface for data entry and exit:
    bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) //数据入队接口
    {
        assert(obj);
        if(myCircularQueueIsFull(obj))
        {
            return false;
        }
        //确保队列没满
        obj->arry[obj->tail]=value;
        obj->tail = (obj->tail + 1)%(obj->capacity +1);
        return true;
    }
    bool myCircularQueueDeQueue(MyCircularQueue* obj)    //数据出队接口
    {
        assert(obj);
        if(myCircularQueueIsEmpty(obj))
        {
            return false;
        }
        //确保队列不为空
        obj->head = (obj->head +1)%(obj->capacity +1);
        return true;
    }

4. Other operation interfaces

The interface that returns the queue head data:

int myCircularQueueFront(MyCircularQueue* obj)   //返回队头数据的接口
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->arry[obj->head];
}

 The interface that returns the data at the end of the queue:

int myCircularQueueRear(MyCircularQueue* obj)   //返回队尾数据的接口     
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    int labelret = ((obj->tail-1)>=0)? obj->tail-1 : obj->capacity;
    //注意tail如果指向数组首地址,则尾数据位于数组最后一个位置
    return obj->arry[labelret];
}

Destroy interface of the queue:

void myCircularQueueFree(MyCircularQueue* obj)     //销毁队列的接口
{
    assert(obj);
    free(obj->arry);
    obj->arry = NULL;
    free(obj);
    obj = NULL;
}

5. Overview of the implementation code of the array circular queue 

 The overall code of the array circular queue:

typedef struct 
{
    int * arry;  //指向堆区数组的指针
    int head;    //队头指针
    int tail;    //队尾指针(指向队尾数据的下一个位置)(不指向有效数据)
    int capacity;//静态队列容量
} MyCircularQueue;


bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);
//顺序编译注意:先被使用而后被定义的函数要记得进行声明

MyCircularQueue* myCircularQueueCreate(int k)          //循环队列初始化接口
{
    MyCircularQueue * tem = (MyCircularQueue *)malloc(sizeof(MyCircularQueue));
    //开辟维护循环队列的结构体
    if(NULL == tem)
    {
        perror("malloc failed");
        exit(-1);
    }
    tem->arry = NULL;
    tem->capacity = k;   
    //队列的数据容量为k
    tem->arry = (int*)malloc((k+1)*sizeof(int));
    //开辟堆区数组
    if(NULL == tem->arry)
    {
        perror("malloc failed");
        exit(-1);
    }
    
    tem->head = 0;
    tem->tail = 0;
    return tem;
}



bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)   //数据入队接口
{
    assert(obj);
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    //确保队列没满
    obj->arry[obj->tail]=value;
    obj->tail = (obj->tail + 1)%(obj->capacity +1);
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj)           //数据出队接口
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    //确保队列不为空
    obj->head = (obj->head +1)%(obj->capacity +1);
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj)               //返回队头数据的接口
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->arry[obj->head];
}

int myCircularQueueRear(MyCircularQueue* obj)                 //返回队尾数据的接口     
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    int labelret = ((obj->tail-1)>=0)? obj->tail-1 : obj->capacity;
    //注意tail如果指向数组首地址,则尾数据位于数组最后一个位置
    return obj->arry[labelret];
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj)              //判断队列是否为空
{
    assert(obj);
    return (obj->tail == obj->head);
}

bool myCircularQueueIsFull(MyCircularQueue* obj)               //判断队列是否为满
{
    assert(obj);
    return ((obj->tail+1)%(obj->capacity +1) == obj->head);
}

void myCircularQueueFree(MyCircularQueue* obj)                 //销毁队列的接口
{
    assert(obj);
    free(obj->arry);
    obj->arry = NULL;
    free(obj);
    obj = NULL;
}

Lituo problem solution test:

3. Static one-way circular linked list realizes circular queue 

Linked list node structure definition:

typedef struct listnode
{
    int data;
    struct listnode * next;
}ListNode;

Maintain the structure of the linked list circular queue:

typedef struct 
{
    int capacity;     //记录队列容量大小
    ListNode * head;  //指向队头节点
    ListNode * tail;  //指向队尾节点
} MyCircularQueue;

1. Structure design of linked list circular queue

The capacity of the static one-way circular linked list is k :

  • Similar to the array circular queue, we also need to create a static circular linked list with k+1 nodes
  • The overall structure diagram of the linked list circular queue: Another situation where the queue is full:
  1.  The full condition of the linked list circular queue (the relational expression for judging whether the queue space is full): tail->next == head;
  2.  Empty judgment condition of the linked list circular queue (relational expression for judging whether the queue is an empty queue): tail == head;

The interface of judging fullness and judging empty of linked list circular queue:

bool myCircularQueueIsEmpty(MyCircularQueue* obj)    //判断队列是否为空
{
    assert(obj);
    return(obj->head == obj->tail);
}

bool myCircularQueueIsFull(MyCircularQueue* obj)     //判断队列是否为满
{
    assert(obj);
    return (obj->tail->next == obj->head);
}

2. Create an interface for a static one-way circular linked list

Implement an interface, create a structure that maintains the circular queue of the linked list and create a static one-way circular linked list with a capacity of k+1:

MyCircularQueue* myCircularQueueCreate(int k)  //循环队列初始化接口
{
    int NodeNum =k+1;                          //创建k+1个链表节点
    MyCircularQueue* object = (MyCircularQueue *)malloc(sizeof(MyCircularQueue));
    assert(object);                            //申请维护循环队列的结构体
    object->capacity = k;

    ListNode * preNode = NULL;                 //用于记录前一个链接节点的地址
    while(NodeNum)
    {
        if(NodeNum == k+1)
        {   ListNode * tem = (ListNode *)malloc(sizeof(ListNode));
            assert(tem);
            preNode = tem;
            object->tail = object->head=tem;    //让tail和head指向同一个初始节点
        }
        else
        {
            ListNode * tem = (ListNode *)malloc(sizeof(ListNode));
            assert(tem);
            preNode->next = tem;                //链接链表节点
            preNode = tem;
        }
        NodeNum--;
    }
    preNode->next = object->head;               //将表尾与表头相接
    return object;
}

3. Data dequeue and enqueue interface

Data entry and exit diagram:

Realize the data entry and exit interface according to the diagram:

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)//数据入队接口(从队尾入队)
{
    assert(obj);
    if(!obj || myCircularQueueIsFull(obj))  //确定队列没满
    {
        return false;
    }           
    obj->tail->data = value;                //数据入队
    obj->tail = obj->tail->next;
    return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj)  //数据出队接口
{
    assert(obj);
    if(!obj || myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    //数据出队
    obj->head = obj->head->next;
    return true;
}

4. Other queue operation interfaces

The interface that returns the queue head data:

int myCircularQueueFront(MyCircularQueue* obj)  //返回队头数据的接口
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->head->data; //返回队头元素
}

The interface that returns the data at the end of the queue:

int myCircularQueueRear(MyCircularQueue* obj)   //返回队尾数据的接口     
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    ListNode * tem = obj->head;
    while(tem->next != obj->tail)               //寻找队尾元素
    {
        tem=tem->next;
    }
    return tem->data;  //返回队尾元素
}

Queue destruction interface:

Diagram of queue destruction process:

void myCircularQueueFree(MyCircularQueue* obj) //销毁队列的接口
{
    assert(obj);
    //利用头指针来完成链表节点的释放
    ListNode * endpoint = obj->head;           //记录一个节点释放的终点
    obj->head = obj->head->next;
    while(obj->head!=endpoint)
    {
        ListNode * tem = obj->head->next;
        free(obj->head);
        obj->head = tem;
    }
    free(endpoint);                            //释放掉终点节点
    free(obj);                                 //释放掉维护环形队列的结构体
}

5. Static linked list circular queue overall code

Overall code:

typedef struct listnode
{
    int data;
    struct listnode * next;
}ListNode;

typedef struct 
{
    int capacity;
    ListNode * head;
    ListNode * tail;
    int taildata;   //单向链表找尾复杂度为O(N),因此我们用一个变量来记录队尾数据
} MyCircularQueue;


bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);
//顺序编译注意:先被使用而后被定义的函数要记得进行声明



MyCircularQueue* myCircularQueueCreate(int k)  //循环队列初始化接口
{
    int NodeNum =k+1;                          //创建k+1个链表节点
    MyCircularQueue* object = (MyCircularQueue *)malloc(sizeof(MyCircularQueue));
    assert(object);                            //申请维护循环队列的结构体
    object->capacity = k;

    ListNode * preNode = NULL;                 //用于记录前一个链接节点的地址
    while(NodeNum)
    {
        if(NodeNum == k+1)
        {   ListNode * tem = (ListNode *)malloc(sizeof(ListNode));
            assert(tem);
            preNode = tem;
            object->tail = object->head=tem;    //让tail和head指向同一个初始节点
        }
        else
        {
            ListNode * tem = (ListNode *)malloc(sizeof(ListNode));
            assert(tem);
            preNode->next = tem;                //链接链表节点
            preNode = tem;
        }
        NodeNum--;
    }
    preNode->next = object->head;               //将表尾与表头相接
    return object;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)   //数据入队接口(从队尾入队)
{
    assert(obj);
    if(!obj || myCircularQueueIsFull(obj))  //确定队列没满
    {
        return false;
    }           
    obj->tail->data = value;                //数据入队
    obj->tail = obj->tail->next;
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj)               //数据出队接口
{
    assert(obj);
    if(!obj || myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    obj->head = obj->head->next;
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj)  //返回队头数据的接口
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->head->data;
}

int myCircularQueueRear(MyCircularQueue* obj)   //返回队尾数据的接口     
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    ListNode * tem = obj->head;
    while(tem->next != obj->tail)               //寻找队尾元素
    {
        tem=tem->next;
    }
    return tem->data;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj)                  //判断队列是否为空
{
    assert(obj);
    return(obj->head == obj->tail);
}

bool myCircularQueueIsFull(MyCircularQueue* obj)                    //判断队列是否为满
{
    assert(obj);
    return (obj->tail->next == obj->head);
}



void myCircularQueueFree(MyCircularQueue* obj) //销毁队列的接口
{
    assert(obj);
    //利用头指针来完成链表节点的释放
    ListNode * endpoint = obj->head;           //记录一个节点释放的终点
    obj->head = obj->head->next;
    while(obj->head!=endpoint)
    {
        ListNode * tem = obj->head->next;
        free(obj->head);
        obj->head = tem;
    }
    free(endpoint);                            //释放掉终点节点
    free(obj);                                 //释放掉维护环形队列的结构体
}

leetcode solution test:

 

 

 

Guess you like

Origin blog.csdn.net/weixin_73470348/article/details/129167702