<Structure de données> Implémentation de file d'attente

teneur

avant-propos

        notion de file d'attente

        La structure de la file d'attente

        Scénarios d'application de file d'attente

Mise en place de la file d'attente

        Créer une structure de file d'attente

        initialisation de la file d'attente

        Destruction de file d'attente

        mettre en file d'attente

        retirer de la file d'attente

        La file d'attente est vide

        Obtenir le nombre d'éléments de file d'attente

        Obtenir l'élément de tête de file d'attente

        Obtenir l'élément à la fin de la file d'attente

code total

        Fichier file d'attente.h

        Fichier file d'attente.c

        Fichier test.c


avant-propos

notion de file d'attente

  • File d' attente : une table linéaire spéciale qui n'autorise les opérations d'insertion de données qu'à une extrémité et les opérations de suppression de données à l'autre extrémité. La file d'attente a un(First In First Out )
  • Mettre en file d'attente : l'extrémité qui effectue l'opération d'insertion est appelée la queue de la file d'attente
  • Retirer de la file d'attente : la fin qui effectue l'opération de suppression est appelée la tête de la file d'attente

Il y a tout de même une certaine différence entre la file d'attente et la pile apprise dans l'article précédent : la file d'attente indique clairement le premier entré, premier sorti. Si l'ordre d'entrée d'une file d'attente est ABCD, alors l'ordre de retrait de la file d'attente doit être ABCD, car si vous entrez puis sortez en A, puis B en entrée et en sortie, puis CD en entrée et en sortie ou similaire, cela n'affectera pas son ordre final Dequeue ABCD. Ceci est toujours différent de la pile, après tout, la pile est LIFO.

La structure de la file d'attente

Scénarios d'application de file d'attente

file d'attente:

  1. file d'attente équitable
  2. Traversée en largeur d'abord...

pile:

  1. résoudre la correspondance des parenthèses
  2. Solveur d'expression polonaise inversée
  3. Récursif à non récursif...

Mise en place de la file d'attente

  • Avant d'implémenter, nous devons d'abord considérer quelle structure utiliser, s'il faut utiliser une structure de tableau ou une structure de chaîne ? La pile que nous avons utilisée ci-dessus est une structure de tableau, devrions-nous également utiliser la file d'attente ?
  • pas vraiment. Une structure chaînée doit être utilisée. Les données de suppression de pile précédentes n'ont pas besoin de déplacer les données, la structure de tableau peut être utilisée pour répondre aux besoins, et la file d'attente doit déplacer les données suivantes vers l'avant lors de la suppression de données, il est très facile d'utiliser la structure de chaîne, changez simplement le point de nœud et le tableau Il est très difficile pour la structure de réaliser le déplacement des données. En résumé, l'utilisation d'une structure en chaîne est optimale. De plus, une liste chaînée peut répondre aux exigences et il n'est pas nécessaire d'utiliser d'autres structures de chaîne plus complexes.

Créer une structure de file d'attente

  •  Idées :

Ici pour définir deux structures, en plus de définir une structure de chaîne pour enregistrer chaque nœud, mais aussi pour définir une structure pour enregistrer la tête et la queue de la file d'attente. De cette manière, il est pratique pour la queue suivante de la file d'attente d'entrer des données et la tête de la file d'attente de sortir des données.

  • Fichier file d'attente.h :
//创建队列结构
typedef int QDataType; //方便后续更改存储数据类型,本文以int为例
 //创建队列节点
typedef struct QueueNode
{
	QDataType data; //存储数据
	struct QueueNode* next; //记录下一个节点
}QNode;
 //保存队头和队尾
typedef struct Queue
{
	QNode* head; //头指针
	QNode* tail; //尾指针
}Queue;

initialisation de la file d'attente

  •  Idées :

La file d'attente peut être vide, mais la structure qui gère les pointeurs de tête et de queue ne peut pas être vide, elle doit donc être affirmée au début. Deuxièmement, avant d'insérer des données, la file d'attente doit être vide, il suffit donc de vider le pointeur de tête et le pointeur de queue.

  • Fichier file d'attente.h :
//初始化队列
void QueueInit(Queue* pq);
  • Fichier file d'attente.c :
//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

Destruction de file d'attente

  •  Idées :

Détruire la file d'attente consiste à détruire toutes les données de la file d'attente, puis vous devez parcourir la liste chaînée pour les détruire gratuitement une par une. Définissez d'abord un pointeur cur comme pq->head, qui est utilisé pour enregistrer les premières données, traverser cur, et s'il n'est pas vide, free. Enfin, laissez la queue et la tête vides.

  • Fichier file d'attente.h :
//销毁队列
void QueueDestory(Queue* pq);
  • Fichier file d'attente.c :
//销毁队列
void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

mettre en file d'attente

  •  Idées :

Entrer dans la file d'attente est en fait très simple. Vous n'avez besoin que d'une insertion de queue. Tout d'abord, vous devez créer un nouveau nœud pour enregistrer les données nouvellement insérées. Mais avant l'insertion de la queue, il faut considérer que si la file d'attente n'a pas de données au début et est vide, il vous suffit de pointer les nœuds de tête et de queue vers le nouveau nœud newnode node. Au contraire, s'il y a des données au début, il suffit de pointer le suivant de la queue vers le nouveau nœud newnode, puis d'affecter le newnode à la queue.

  • Fichier file d'attente.h :
//入队列
void QueuePush(Queue* pq, QDataType x);
  •  Fichier file d'attente.c :
//入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	//创建一个新节点保存数据
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	//暴力检测newnode,因为malloc的都要检测
	assert(newnode);
	newnode->next = NULL;
	newnode->data = x;
	//如果一开始没有数据,为空的情况
	if (pq->tail == NULL)
	{
		assert(pq->head == NULL);
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

retirer de la file d'attente

  •  Idées :

Cas spéciaux:

Ici, lors de la suppression de données, nous devons d'abord considérer des cas particuliers. Lorsqu'il ne reste qu'une seule donnée, supprimez-la à nouveau. À ce stade, les données ont disparu, mais la tête est vide et la queue devient un pointeur sauvage. Afin de éviter ce phénomène La génération de , discutée séparément et tête et queue vides.

en général:

À ce stade, il vous suffit de définir un pointeur suivant pour enregistrer le nœud suivant de la tête, déplacer la tête vers la suivante et vider l'ancienne tête.

  •  Fichier file d'attente.h :
//出队列
void QueuePop(Queue* pq);
  • Fichier file d'attente.c :
//出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head && pq->tail); //tail和head均不能为空
	//特殊:当删到head=tail的位置时
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	//一般情况
	else
	{
		//保存head的下一个节点
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

La file d'attente est vide

  •  Idées :

Si la tête est vide ou la queue est vide, c'est une condition vide, il suffit de revenir directement.

  • Fichier file d'attente.h :
//判空
bool QueueEmpty(Queue* pq);
  • Fichier file d'attente.c :
//判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

Obtenir le nombre d'éléments de file d'attente

  •  Idées :

Il n'est pas difficile de trouver le nombre d'éléments, il suffit de définir un pointeur cur comme première donnée pq->head, et de définir la variable size pour enregistrer le nombre. Traverse cur à son tour, s'il n'est pas vide, la taille est ++. L'idée de ​​​​cette traversée n'est pas compliquée, mais la complexité temporelle atteint O(N), ce qui n'est pas très bon. Si vous voulez O(1), vous pouvez directement définir une variable de taille supplémentaire lorsque vous définissez la structure au début, qui est spécialement utilisé pour enregistrer le nombre d'éléments valides. Nombre, à chaque fois dans la taille de la file d'attente++, hors de la taille de la file d'attente--. Cette implémentation est meilleure, mais afin de l'encapsuler dans un module indépendant, la méthode de traversée est toujours utilisée. comme suit:

  • Fichier file d'attente.h :
//获取有效元素个数
size_t QueueSize(Queue* pq);
  • Fichier file d'attente.c :
//获取有效元素个数
size_t QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	size_t size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

Obtenir l'élément de tête de file d'attente

  •  Idées :

Tout d'abord, il est nécessaire d'affirmer que la tête ne peut pas être vide. Si la tête est vide, comment pouvons-nous obtenir l'élément de tête, puis retourner directement les données de la tête.

  • Fichier file d'attente.h :
//获取队头元素
QDataType QueueFront(Queue* pq);
  • Fichier file d'attente.c :
//获取队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head); //头部不能为空
	return pq->head->data;
}

Obtenir l'élément à la fin de la file d'attente

  •  Idées :

Avec l'expérience de l'obtention des éléments de la tête de la file d'attente, la queue de la file d'attente est plus simple, il suffit de changer la tête en queue et la structure est la même que ci-dessus.

  • Fichier file d'attente.h :
//获取队尾元素
QDataType QueueBack(Queue* pq);
  • Fichier file d'attente.c :
//获取队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->tail); //尾部不能为空
	return pq->tail->data;
}

code total

Fichier file d'attente.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

//创建队列结构
typedef int QDataType; //方便后续更改存储数据类型,本文以int为例
 //创建队列节点
typedef struct QueueNode
{
	QDataType data; //存储数据
	struct QueueNode* next; //记录下一个节点
}QNode;
 //保存队头和队尾
typedef struct Queue
{
	QNode* head; //头指针
	QNode* tail; //尾指针
}Queue;

//初始化队列
void QueueInit(Queue* pq);

//销毁队列
void QueueDestory(Queue* pq);

//入队列
void QueuePush(Queue* pq, QDataType x);

//出队列
void QueuePop(Queue* pq);

//判空
bool QueueEmpty(Queue* pq);

//获取有效元素个数
size_t QueueSize(Queue* pq);

//获取队头元素
QDataType QueueFront(Queue* pq);

//获取队尾元素
QDataType QueueBack(Queue* pq);

Fichier file d'attente.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"

//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

//销毁队列
void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

//入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	//创建一个新节点保存数据
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	//暴力检测newnode,因为malloc的都要检测
	assert(newnode);
	newnode->next = NULL;
	newnode->data = x;
	//如果一开始没有数据,为空的情况
	if (pq->tail == NULL)
	{
		assert(pq->head == NULL);
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

//出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head && pq->tail); //tail和head均不能为空
	//特殊:当删到head=tail的位置时
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	//一般情况
	else
	{
		//保存head的下一个节点
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

//判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

//获取有效元素个数
size_t QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	size_t size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

//获取队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head); //头部不能为空
	return pq->head->data;
}

//获取队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->tail); //尾部不能为空
	return pq->tail->data;
}

Fichier test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
void TestQueue()
{
	Queue q;
	QueueInit(&q);
	//插入数据
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	//打印
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");
}
int main()
{
	TestQueue();
	return 0;
}

Je suppose que tu aimes

Origine blog.csdn.net/bit_zyx/article/details/123967787
conseillé
Classement