Page d'accueil personnelle : Bienvenue à tous --> Populus euphratica sous le désert
Les gars, vous êtes si beaux
Si vous trouvez l'article utile
Vous pouvez soutenir les blogueurs en un clic
Chaque once de votre préoccupation est la force motrice pour moi de persévérer
![]()
☄ : Le sujet de ce numéro : Implémentation d'une liste chaînée simple dans le chapitre sur la structure des données
J'espère que vous avez tous une bonne étude et que vous travaillez tous les jours.
![]()
Apprenez la structure des données, apprenez d'abord à dessiner, commençons le voyage de peinture avec Xiao Huyang dans ce numéro !
teneur
Qu'est-ce qu'une liste chaînée simple ?
Stockage de l'adresse réelle de la liste chaînée
Supprimez l'en-tête de la liste liée :
Suppression de la fin de la liste liée
Rechercher un élément de liste liée :
Modifier les éléments de la liste liée
Insérer après n'importe quelle position dans la liste liée
Ajouter n'importe où dans la liste liée
Supprimer après n'importe quelle position dans la liste liée
Supprimer n'importe quelle position dans la liste liée
Impression de la liste chaînée
À propos du fichier d'en-tête de code global :
À propos du fichier source de code global :
Qu'est-ce qu'une liste chaînée simple ?
Une liste chaînée est une structure de stockage non consécutive et non séquentielle en termes de structure de stockage physique. L'ordre logique des éléments de données est réalisé par l'ordre des liens des pointeurs dans la liste chaînée.
Semblable à la concaténation d'une chaîne.
Stockage de l'adresse réelle de la liste chaînée
Notre schéma général de la liste chaînée est le suivant :
En fait, nous devons regarder l'adresse réelle de la liste chaînée pour stocker :
code afficher comme ci-dessous:
SLTNode* d1 = (SLTNode*)malloc(sizeof(SLTNode)); assert(d1); SLTNode* d2 = (SLTNode*)malloc(sizeof(SLTNode)); assert(d2); SLTNode* d3 = (SLTNode*)malloc(sizeof(SLTNode)); assert(d3); d1->data = 1; d2->data = 2; d3->data = 3; d1->next = d2; d2->next = d3; d3->next = NULL; SListPrint(d1);
Il s'agit de l'adresse réelle de la liste à liens simples
Créer une liste liée
Tout d'abord, nous devons d'abord créer une liste chaînée et utiliser la structure pour la créer, ce qui n'est pas grand-chose à dire.Les détails de la définition de la structure de ce fichier d'en-tête sont tous dans le code. Si vous ne comprenez pas la structure, veuillez passer à ☞ Explication détaillée de la structure
#pragma once//防止头文件重复包含 #define _CRT_SECURE_NO_WARNINGS//防止scanf函数报错 #include <stdio.h> #include <stdlib.h> #include <assert.h>//断言头文件 typedef int SLTDataType;//把int重命名 typedef struct SListNode//结构体重命名 { SLTDataType data; struct SListNode *next;//这个不可以使用重命名后的名字 }SLTNode;
noeud d'application
Nous avons une liste liée, mais nous n'avons pas encore de membres de la liste liée ! Nous devons donc postuler pour un nœud, quand devons-nous postuler pour un nœud ? Cela doit être le choix d'insert aura besoin du node , donc autant écrire la fonction à appliquer directement pour le node, et attacher les données, le pointeur que nous pouvons pointer vers NULL par défaut .
Regardez le code :
SLTNode* BuySListNode(SLTDataType x) { SLTNode* newnode = (SLTNode *)malloc(sizeof(SLTNode)); assert(newnode); newnode->next = NULL; newnode->data = x; return newnode; }
Nous pouvons passer directement la valeur à attribuer à la fonction, puis ouvrir un espace pour un nœud, faire une assertion, déterminer si l'espace est demandé, puis attribuer la valeur à attribuer aux données et pointer le pointeur de nœud à NUL.
fin de la liste chaînée
Tout d'abord, réfléchissons à quels paramètres passer ?
La première est la valeur à insérer, puis l'adresse de la liste chaînée, ou la liste chaînée directement ? Que faire si la liste chaînée est NULL ?
Regardons d'abord un diagramme schématique:
Nous avons déjà un nœud et une valeur a été placée, alors comment faire ? Nous avons seulement besoin de pointer le NULL de next dans d3 vers le nœud nouvellement ouvert, n'est-ce pas ? Regardez ensuite l'image :
Ça peut être comme ça. (Le plus important est que le plist n'a pas changé)
Mais que se passe-t-il si notre liste chaînée ne contient aucun élément ? Regardez ensuite l'image :
À ce moment, le plist est NULL, donc pointons simplement le plist vers l'adresse de d, et il n'y a qu'un seul d dont le nœud suivant est NULL. (Remarque : plist est passé d'un pointeur nul à pointer vers le nouveau nœud d)
Jetez un oeil au code (beaucoup de gens auront tort de le démontrer) :
void SListPushBack(SLTNode *pphead, SLTDataType x) { SLTNode* newnode = BuySListNode(x); if (pphead == NULL) { pphead = newnode; } else { SLTNode* cur = pphead; while (cur->next) { cur = cur->next; } cur->next = newnode; } }
Y a-t-il quelque chose de mal avec cette façon d'écrire?
Nous avons débogué et constaté que le plist n'a pas changé et que la liste liée n'a pas été insérée , puis regardez l'image :
Nous sommes arrivés à une conclusion :
Au lieu de passer un pointeur, vous pouvez changer ce vers quoi ce pointeur pointe. Nous devrions passer la forme d'un pointeur de second niveau, puis le déréférencer pour obtenir l'effet de changer l'adresse plist. Par conséquent, toutes les fonctions d'insertion et de suppression de notre liste chaînée passe le pointeur de niveau 2 et passe le pointeur de niveau 2. Lorsque vous utilisez un pointeur vers le premier niveau, nous avons juste besoin de le déréférencer en utilisant le nœud principal.
Donc le bon code est :
void SListPushBack(SLTNode **pphead, SLTDataType x) { SLTNode* newnode = BuySListNode(x); if (*pphead == NULL) { *pphead = newnode; } else { SLTNode* cur = *pphead; while (cur->next) { cur = cur->next; } cur->next = newnode; } }
Rien d'autre n'a changé, sauf que l'endroit où l'en-tête de liste liée est utilisé est modifié pour déréférencer l'en-tête de liste liée.
en-tête de liste liée
Nous devons encore considérer la question de savoir si le plist a changé ?
C'est sûr, la prise de tête sert à changer de tête ! C'est-à-dire que la tête de la liste chaînée a été modifiée, nous devons donc transmettre l'adresse.
Regardons l'image pour comprendre :
C'est le cas au début, nous voulons mettre l'adresse du plist dans le nouveau nœud, et changer le plist à l'adresse du nouveau nœud.
Regardons le schéma ci-dessous :
Nous n'avons pas plist, mais nous avons pphead, et le déréférencement direct est le nœud principal.
Points à noter :
L'étape 1 doit être effectuée en premier, puis l'étape 2. Sinon, notre nœud principal sera écrasé, et aucun futur nœud ne sera trouvé, nous devons donc d'abord effectuer l'étape 1, puis l'étape 2.
La figure suivante montre les conséquences de l'exécution de l'étape 2 en premier et de l'exécution de l'étape 1 (boucle infinie) :
Voici le bon code :
void SListPushFrond(SLTNode **pphead, SLTDataType x) { SLTNode* newnode = BuySListNode(x); newnode->next = *pphead; *pphead = newnode; }
Supprimez l'en-tête de la liste liée :
La suppression de la tête de la liste chaînée est très simple, on passe quand même le pointeur secondaire, car s'il n'y a qu'un seul nœud, il devient NULL après suppression, et le point de la liste chaînée est toujours modifié.
Voir le schéma ci-dessous :
![]()
Il nous suffit de pointer le nœud principal d'origine vers le deuxième nœud, puis de libérer le nœud principal d'origine.
il faut savoir c'est :
Nous devons d'abord créer un pointeur de nœud pour enregistrer l'adresse du deuxième nœud
Ensuite, nous devons libérer le pointeur de tête d'origine (*pphead)
Attribuez ensuite l'adresse du deuxième nœud à *pphead, ce qui en fait le pointeur principal de la nouvelle liste chaînée.
S'il s'agit d'un nœud, c'est également possible, car le nœud suivant de *pphead est NULL
Nous stockons d'abord NULL, puis libérons le nœud *pphead
Ensuite, mettez NULL dans *pphead, il devient NULL et remplit la condition.
Voici le code :
void SListPopFrond(SLTNode **pphead) { assert(pphead); SLTNode* next = (*pphead)->next; free(*pphead); *pphead = next; }
Suppression de la fin de la liste liée
La suppression de la queue de la liste chaînée doit être davantage prise en compte. S'il existe un nœud, il est analysé séparément de plusieurs nœuds.
Ou regardez d'abord l'image:
Si tel est le cas, libérez-le directement, puis définissez-le sur NULL.
Dans ce cas, il faut libérer le nœud d3, puis pointer le nœud d2 sur NULL.
Alors, comment trouve-t-on d2 ? Regardez ensuite l'image :
![]()
![]()
![]()
Il s'agit de la logique réelle d'écriture du code. Voici le code. La partie commentaire dans le code est la deuxième méthode, en accédant deux fois au pointeur suivant :
void SListPopBack(SLTNode **pphead) { assert(*pphead); if ((*pphead)->next == NULL) { free(*pphead); *pphead = NULL; } else { //SLTNode *cur = *pphead; //while (cur->next->next != NULL) //{ // cur = cur->next; //} //free(cur->next); //cur->next = NULL; SLTNode *tail = *pphead; SLTNode *tailprev = NULL; while (tail->next != NULL) { tailprev = tail;//失误三 tail = tail->next; } free(tail); tailprev->next = NULL; } }
Rechercher un élément de liste liée :
La recherche est très simple, nous venons de forcer brutalement la recherche :
Regardez le code :
SLTNode* SListFind(SLTNode *pphead, SLTDataType x) { SLTNode* cur = pphead; while (cur) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; }
Points à noter :
Tout d'abord, nous devons d'abord enregistrer le pointeur de tête, ne changez pas le pointeur de tête.
Deuxièmement, nous devons considérer la robustesse du code et l'utiliser avec d'autres interfaces fonctionnelles, nous définissons donc la valeur de retour comme un pointeur de nœud. S'il n'est pas trouvé, nous renvoyons NULL.
Modifier les éléments de la liste liée
Il est aussi très simple de le modifier et de l'utiliser avec la recherche, il suffit de regarder le code :
On peut apporter des modifications grâce aux indices trouvés, rien à dire.
void SListModify(SLTNode *pos, SLTDataType x) { assert(pos); pos->data = x; }
Insérer après n'importe quelle position dans la liste liée
Insérer après n'importe quelle position dans la liste liée, nous devons d'abord trouver la position, puis insérer.
Regardez d'abord l'image :
On connait cette position d'insertion, il suffit ensuite de changer le pointage , puis de regarder l'image :
Ce à quoi nous devons faire attention, c'est d'effectuer d'abord l'étape 1, et à l'étape 2, l'ordre ne doit pas être inversé, sinon ce sera une boucle infinie comme une prise de tête.
Voici le code spécifique :
void SListInsertAfter(SLTNode *pos, SLTDataType x) { assert(pos); SLTNode* newnode = BuySListNode(x);//注意下面代码的位置 newnode->next = pos->next; pos->next = newnode; }
Ajouter n'importe où dans la liste liée
L'insertion après n'importe quelle position dans la liste chaînée est plus compliquée. Voici une petite analyse :
Tout d'abord, s'il n'y a qu'un seul nœud, cela revient à brancher la tête.
Tout comme la prise de tête, je ne le répéterai pas ~
S'il y a plusieurs nœuds, ce sera compliqué ~ Regardons d'abord l'image :
Comment connaître la valeur du nœud précédent à cette position ? La méthode est en fait la même que la suppression de la queue, elle utilise une variable à traverser, et la condition de fin de boucle devient p1->next != cette position.
Regardez l'image ci-dessous:
Enfin regarde le code :
void SListInsertBefore(SLTNode **pphead, SLTNode *pos, SLTDataType x) { assert(pos); if (pos == *pphead) { SListPushFrond(pphead, x); } else { SLTNode *prve = *pphead; while (prve->next != pos) { prve = prve->next; } SLTNode* newnode = BuySListNode(x); newnode->next = pos; prve->next = newnode; } }
Supprimer après n'importe quelle position dans la liste liée
Supprimer n'importe quelle position est relativement simple, nous n'avons qu'à faire attention à une chose,
Autrement dit, lorsque l'élément de la liste liée est 1, il n'y a pas de suivi et il ne peut pas être supprimé.
Ou regardez l'image ci-dessous:
Il n'y a qu'un seul nœud, les éléments suivants ne peuvent pas être supprimés, les autres sont directement écrasés, puis le nœud à la position pos est libéré
Regardons directement le code :
void SListEraseAfter(SLTNode *pos) { assert(pos); SLTNode *next = pos->next; if (next) { pos->next = next->next; free(next); next = NULL; } }
Supprimer n'importe quelle position dans la liste liée
C'est un peu plus gênant, analysons-le un peu:
Tout d'abord, si cette position est en tête de liste chaînée, alors on supprime la tête,
Ou regardez la photo :
S'il est supprimé à d'autres positions, cela revient à l'insérer à n'importe quelle position. Nous devons créer un nœud pointeur et le parcourir. La condition de fin de boucle est p1->next != pos, puis liez le liste chaînée brisée.
Regardez ensuite l'image :
Voici le code :
void SListErase(SLTNode **pphead, SLTNode *pos) { assert(pphead); assert(pos); if (*pphead == pos) { SListPopFrond(pphead); } else { SLTNode *prve = *pphead; while (prve->next != pos) { prve = prve->next; } prve->next = pos->next; free(pos); pos = NULL; } }
Impression de la liste chaînée
On peut imprimer sans passer l'adresse de la liste chaînée, on ne modifiera pas le contenu de la liste chaînée, et un autre point est de créer des variables temporaires à parcourir.
Regardons le code :
void SListPrint(SLTNode* pphead) { SLTNode* cur = pphead; while (cur) { printf("%d->", cur->data); cur = cur->next; } printf("NULL\n"); }
Espace de liste libre
Ce n'est pas mentionné, nous devons toujours passer l'adresse de la liste liée, car nous devons définir la liste liée sur NULL
Regarde juste le code :
void SListDestroy(SLTNode **pphead) { assert(pphead); SLTNode *cur = *pphead; while (cur) { SLTNode *next = cur->next; free(cur); cur = next; } *pphead = NULL; }
À propos du fichier d'en-tête de code global :
#pragma once #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <assert.h> typedef int SLTDataType; typedef struct SListNode { SLTDataType data; struct SListNode *next; }SLTNode; void SListPrint(SLTNode* pphead);//打印 SLTNode* BuySListNode(SLTDataType x);//申请新节点 void SListPushBack(SLTNode **pphead, SLTDataType x);//尾插 void SListPushFrond(SLTNode **pphead, SLTDataType x);//头插 void SListPopFrond(SLTNode **pphead);//头删 void SListPopBack(SLTNode **pphead);//尾删 SLTNode* SListFind(SLTNode *pphead, SLTDataType x);//查找 void SListInsertBefore(SLTNode **pphead, SLTNode *pos, SLTDataType x);//任意位置前插 void SListInsertAfter(SLTNode *pos, SLTDataType x);//任意位置后插 void SListErase(SLTNode **pphead, SLTNode *pos);//删除任意位置 void SListEraseAfter(SLTNode *pos);//任意位置后删 void SListDestroy(SLTNode **pphead);//释放链表 void SListModify(SLTNode *pos, SLTDataType x);//修改链表
À propos du fichier source de code global :
#include "SList.h" void SListPrint(SLTNode* pphead) { SLTNode* cur = pphead; while (cur) { printf("%d->", cur->data); cur = cur->next; } printf("NULL\n"); } SLTNode* BuySListNode(SLTDataType x) { SLTNode* newnode = (SLTNode *)malloc(sizeof(SLTNode)); assert(newnode); newnode->next = NULL; newnode->data = x; return newnode; } void SListPushBack(SLTNode **pphead, SLTDataType x) { SLTNode* newnode = BuySListNode(x); if (*pphead == NULL) { *pphead = newnode; } else { SLTNode* cur = *pphead; while (cur->next) { cur = cur->next; } cur->next = newnode; } } void SListPushFrond(SLTNode **pphead, SLTDataType x) { SLTNode* newnode = BuySListNode(x); newnode->next = *pphead; *pphead = newnode; } void SListPopBack(SLTNode **pphead) { assert(*pphead); if ((*pphead)->next == NULL) { free(*pphead); *pphead = NULL; } else { //方法二 //SLTNode *cur = *pphead; //while (cur->next->next != NULL) //{ // cur = cur->next; //} //free(cur->next); //cur->next = NULL; SLTNode *tail = *pphead; SLTNode *tailprev = NULL; while (tail->next != NULL) { tailprev = tail; tail = tail->next; } free(tail); tailprev->next = NULL; } } void SListPopFrond(SLTNode **pphead) { assert(pphead); SLTNode* next = (*pphead)->next; free(*pphead); *pphead = next; } SLTNode* SListFind(SLTNode *pphead, SLTDataType x) { SLTNode* cur = pphead; while (cur) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; } void SListInsertBefore(SLTNode **pphead, SLTNode *pos, SLTDataType x) { assert(pos); if (pos == *pphead) { SListPushFrond(pphead, x); } else { SLTNode *prve = *pphead; while (prve->next != pos) { prve = prve->next; } SLTNode* newnode = BuySListNode(x); newnode->next = pos; prve->next = newnode; } } void SListInsertAfter(SLTNode *pos, SLTDataType x) { assert(pos); SLTNode* newnode = BuySListNode(x);//注意下面代码的位置 newnode->next = pos->next; pos->next = newnode; } void SListErase(SLTNode **pphead, SLTNode *pos) { assert(pphead); assert(pos); if (*pphead == pos) { SListPopFrond(pphead); } else { SLTNode *prve = *pphead; while (prve->next != pos) { prve = prve->next; } prve->next = pos->next; free(pos); pos = NULL; } } void SListEraseAfter(SLTNode *pos) { assert(pos); SLTNode *next = pos->next; if (next) { pos->next = next->next; free(next); next = NULL; } } void SListDestroy(SLTNode **pphead) { assert(pphead); SLTNode *cur = *pphead; while (cur) { SLTNode *next = cur->next; free(cur); cur = next; } *pphead = NULL; } void SListModify(SLTNode *pos, SLTDataType x) { assert(pos); pos->data = x; }
———————————————————————————————————————————