Structure de données : implémentation d'une file d'attente circulaire (leetcode622. Concevoir une file d'attente circulaire)

 

Table des matières

1. Une brève introduction aux files d'attente circulaires

2. Implémenter une file d'attente circulaire avec un tableau statique

1. Conception de la structure de la file d'attente circulaire du tableau

2. L'interface d'application de la mémoire de la zone de tas de la file d'attente circulaire du tableau 

3. Réalisation d'interface de retrait et de mise en file d'attente de données

4. Autres interfaces de fonctionnement

5. Présentation du code d'implémentation de la file d'attente circulaire du tableau 

3. La liste liée circulaire à sens unique statique réalise une file d'attente circulaire 

1. Conception de la structure de la file d'attente circulaire de la liste chaînée

2. Créer une interface pour une liste liée circulaire à sens unique statique

3. Interface de retrait et de mise en file d'attente des données

4. Autres interfaces d'opération de file d'attente

5. Code global de file d'attente circulaire de liste liée statique


Source du problème : 622. Conception d'une file d'attente circulaire - Leetcode

1. Une brève introduction aux files d'attente circulaires

  • Une file d'attente circulaire est généralement une structure de données linéaire statique dans laquelle les données sont conformes au principe du premier entré, premier sorti .
  • L'adresse de tête de conteneur et l'adresse de queue de conteneur de la file d'attente circulaire sont connectées via des opérations spécifiques (telles que la liaison de pointeurs, l'indice de tableau , etc. ), réalisant ainsi la réutilisation de l'espace du conteneur ( dans une file d'attente statique acyclique , une fois qu'une file d'attente est pleine, nous ne pouvons pas insérer l'élément suivant, même s'il y a encore de la place au début de la file d'attente )

 

2. Implémenter une file d'attente circulaire avec un tableau statique

Maintenez la structure de la file d'attente :

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

1. Conception de la structure de la file d'attente circulaire du tableau

Nous supposons que la capacité du tableau statique est k ( peut stocker k données ) :

  • Selon la structure de données de base de la file d'attente : il y a deux pointeurs utilisés pour maintenir l'espace de données effectif dans le tableau , à savoir le pointeur de tête et le pointeur de queue, le pointeur de tête est utilisé pour pointer vers les données en tête de la file d'attente , et la queue est utilisée pour pointer vers la position suivante des données à la fin de la file d'attente (c'est-à-dire que le pointeur de queue ne pointe pas vers des données valides)
  •  Comme le montre la figure, l'espace mémoire pour les données valides se situe entre le pointeur de tête et le pointeur de queue
  • La relation entre le pointeur de tête et le pointeur de queue est utilisée pour réaliser le jugement de remplissage de la file d'attente ( juger si l'espace de file d'attente est plein ) et le jugement de vide ( juger si la file d'attente est vide ) ; afin d'atteindre cet objectif, nous devons définir la capacité du tableau statique 
  1. Condition de file d'attente vide : queue == tête ;
  2. Condition de file d'attente pleine : (tail+1)%(k+1) == head ;  Autre situation :
  • A partir de là, nous pouvons d'abord concevoir l'interface pleine et vide de la file d'attente :
    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. L'interface d'application de la mémoire de la zone de tas de la file d'attente circulaire du tableau 

  • Créez une structure MyCircularQueue sur le tas et appliquez un tableau de (k+1)*sizeof(DataType) octets pour la file d'attente en même temps :
    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. Réalisation d'interface de retrait et de mise en file d'attente de données

Schéma de retrait et de mise en file d'attente des données :

 

  •  Selon le schéma, nous pouvons concevoir l'interface d'entrée et de sortie des données :
    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. Autres interfaces de fonctionnement

L'interface qui renvoie les données d'en-tête de file d'attente :

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

 L'interface qui renvoie les données à la fin de la file d'attente :

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];
}

Détruisez l'interface de la file d'attente :

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

5. Présentation du code d'implémentation de la file d'attente circulaire du tableau 

 Le code global de la file d'attente circulaire du tableau :

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;
}

Test de solution de problème Lituo :

3. La liste liée circulaire à sens unique statique réalise une file d'attente circulaire 

Définition de la structure du nœud de la liste chaînée :

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

Maintenez la structure de la file d'attente circulaire de la liste chaînée :

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

1. Conception de la structure de la file d'attente circulaire de la liste chaînée

La capacité de la liste chaînée circulaire unidirectionnelle statique est k :

  • Semblable à la file d'attente circulaire du tableau, nous devons également créer une liste liée circulaire statique avec k + 1 nœuds
  • Schéma de la structure globale de la file d'attente circulaire de liste chaînée : autre situation où la file d'attente est pleine :
  1.  La condition complète de la file d'attente circulaire de la liste chaînée (l'expression relationnelle pour juger si l'espace de la file d'attente est plein) : tail->next == head;
  2.  Condition de jugement vide de la file d'attente circulaire de liste chaînée (expression relationnelle pour juger si la file d'attente est une file d'attente vide) : tail == head;

L'interface d'évaluation de la plénitude et d'évaluation du vide de la file d'attente circulaire de la liste chaînée :

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

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

2. Créer une interface pour une liste liée circulaire à sens unique statique

Implémentez une interface, créez une structure qui maintient la file d'attente circulaire de la liste chaînée et créez une liste chaînée circulaire unidirectionnelle statique d'une capacité de 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. Interface de retrait et de mise en file d'attente des données

Diagramme d'entrée et de sortie de données :

Réaliser l'interface de saisie et de sortie de données selon le schéma :

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. Autres interfaces d'opération de file d'attente

L'interface qui renvoie les données d'en-tête de file d'attente :

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

L'interface qui renvoie les données à la fin de la file d'attente :

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;  //返回队尾元素
}

Interface de destruction de file d'attente :

Schéma du processus de destruction de file d'attente :

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. Code global de file d'attente circulaire de liste liée statique

Code général :

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);                                 //释放掉维护环形队列的结构体
}

test de solution leetcode :

 

 

 

Je suppose que tu aimes

Origine blog.csdn.net/weixin_73470348/article/details/129167702
conseillé
Classement