Artikelverzeichnis
-
- Vorschläge hervorbringen
- selbstreferenzierende Struktur
- verlinkte Liste
-
- Struktur einer verknüpften Liste
- Erstellung verknüpfter Listen, Einfügen und Löschen von Knoten
- Grundlegende Funktionsweise einer verknüpften Liste
Vorschläge hervorbringen
Lesen Sie die Informationen mehrerer Studierender ein und verarbeiten Sie diese. Da die Anzahl der Schüler unbekannt ist, wird dynamisch zugewiesener Speicher zum Speichern von Schülerinformationen verwendet
struct student{ /*学生信息结构类型*/
char nu[7]; /*学号*/
char name[9]; /*姓名*/
} ;
main()
{
char nu[7];
struct student * ptr;
printf("请输入学生学号");
gets(nu);//读入学号
while(strcmp(nu,"0000")!=0){//判断学号是否合法
ptr = malloc(sizeof(struct student)) ;
strcpy(ptr->no,no);//学号复制到内存中
printf("请输入学生姓名");
gets(ptr->name); //读取姓名到内存中
printf("请输入学生学号");
gets(nu);
}
...
}
Beteiligte Funktionen:
gets()
,strcmp()
,strcpy
.
Bestehendes Problem : Da der Speicher dynamisch zum Speichern von Schülerinformationen zugewiesen wird, ist der Speicher, in dem die einzelnen Schülerinformationen gespeichert werden, normalerweise diskontinuierlich . Wie kann auf diese Schülerinformationen zugegriffen werden?
Lösung 1: Speichern Sie die Speicheradresse der Schülerinformationen im Zeiger-Array.
Es gibt ein Problem: Die Länge des Zeiger-Arrays ist immer noch festgelegt
Tragen Sie Fragmente in Ketten auf und verwandeln Sie physikalisch diskontinuierliche Fragmente in logisch kontinuierliche Fragmente
• Wie werden Speicherfragmente verkettet?
• Verwendung von Zeigern – Der Kern von Zeigern besteht darin, Adressen zu speichern
• Nutzen Sie die Struktur als kleinste Einheit der Speicherzuordnung
• Definieren Sie eine Komponente (Mitglied), die Zeiger in der Struktur speichert
• Nutzen Sie den dynamischen Speicherzuweisungsmechanismus, um Strukturspeicher zuzuweisen
selbstreferenzierende Struktur
Eine Variable vom Typ kann struct book
in einer Struktur nicht neu definiert werden.struct book
Es ist jedoch möglich, struct book
Zeiger auf Typen zu definieren: struct book*
(bekannt als selbstreferenzielle Strukturen ).
Eine selbstreferenzierende Struktur enthält ein Zeigermitglied auf eine Struktur desselben Typs wie sie selbst. wie:
struct node{
int data;
struct node* pNext;
}
Dazu gehört struct node*
die selbstreferenzielle Struktur.
pNext
Es wird als Link (Link) bezeichnet und dient dazu, eine struct node
Strukturvariable eines Typs mit einer anderen Strukturvariablen desselben Typs zu verbinden.
struct node n1,n2;
n1.pNext=&n2;
verlinkte Liste
Struktur einer verknüpften Liste
Schüler Informationen:
typedef struct student{
//数据域
char num[7];//学号
char name[9];//姓名
//指针域
struct student* pNext;//记录下一个
}
Wenden Sie 3 Strukturvariablen dynamisch an, um die Informationen von 3 Schülern zu speichern, und die 3 Strukturvariablen werden durch Zeiger mit einer Vorgänger- und Nachfolgerbeziehung miteinander „verkettet“ . Die Adresse der ersten Strukturvariablen wird separat in einem Zeiger festgehalten .
verlinkte Liste
-
Definition:
Es handelt sich um eine lineare Sammlung von **selbstreferenziellen Strukturvariablen (sogenannte Knoten)**, die durch Linkzeiger miteinander verkettet sind, und eine Speicherstruktur aus linearen Tabellen.
-
Struktur einer verknüpften Liste
- Knoten : Ein Punkt in einem Stromkreis, der drei oder mehr Zweige verbindet
- Knoten : Der Endpunkt oder Schnittpunkt einer Linie oder Kurve
- Kopfknoten :
pHead
– Zeigervariable, die auf den Kopfknoten zeigt - Jeder Knotenpunkt besteht aus 2 Feldern:
- Datenfeld – speichert die Informationen des Knotens selbst.
- Zeigerfeld – speichert Zeiger auf nachfolgende Knoten (für einfach verknüpfte Listen).
- Das Zeigerfeld des Endknotens wird als Zeichen für das Ende der verknüpften Liste auf NULL gesetzt (dargestellt durch einen Backslash).
3. Die Merkmale der verknüpften Liste:
- Eine verknüpfte Liste ist eine Speicherstruktur, die zum Speichern linearer Listen verwendet wird.
- Die Knoten (Knoten) der verknüpften Liste werden durch Aufrufen der dynamischen Speicherzuweisungsfunktion entsprechend den Anforderungen zugewiesen, sodass die verknüpfte Liste nach Bedarf gedehnt und gekürzt werden kann , wodurch Speicher gespart wird, wenn die Anzahl der zu speichernden Daten unbekannt ist;
- Die Knoten der verknüpften Liste sind logisch kontinuierlich, aber der Speicher jedes Knotens ist normalerweise diskontinuierlich , sodass nicht sofort darauf zugegriffen werden kann, sondern nur Knoten für Knoten, beginnend mit dem Kopfknoten .
Erstellung verknüpfter Listen, Einfügen und Löschen von Knoten
1. Die Erstellung einer verknüpften Liste
Von Grund auf werden Knoten nacheinander erstellt und verbunden (verknüpft). Schritte:
- Header-Zeiger deklarieren
- Knoten (Struktur) generieren
- Link (Kopfzeile einfügen oder am Ende anhängen)
- geh im Kreis und im Kreis
Dynamische Speicherzuweisung von Knoten:
pNew = malloc(sizeof(struct node));
pNew->data = 17;
Die dynamische Zuordnung verknüpfter Listenknoten bestimmt, dass die Speicherorte der Knoten im Speicher nicht unbedingt kontinuierlich sind.
typedef struct ListNode {
//ListNode:链表节点
int data; //定义data变量值,存储节点值
struct ListNode *next; //定义next指针,指向下一个节点,维持节点连接
} LISTNODE;
LISTNODE* creat_FIFO_List(){
//FIFO( First Input First Output)先进先出
//step1:变量定义;
//step2:变量初始化;
//stept3:创建节点
scanf("%d",&num);
while(num != -1){
pNew = malloc(sizeof(LISTNODE));
if(pNew != NULL){
pNew->data = num;
//step4:细化:将新节点插入链表
}
scanf("%d",&num);
}
return pHead;
}
Wenn der erste Knoten erstellt wird, wird pHead
auf ihn verwiesen;
Andernfalls wird der neu hinzugefügte Knoten an den Endknoten der verknüpften Liste angehängt.
Damit neue Knoten bequem an den Endknoten der verknüpften Liste angehängt werden können, muss ein Zeiger entworfen werden, der pTrail
auf den Endknoten der verknüpften Liste zeigt.
Anhängevorgang:
pTrail->pNext = pNew;
pTtrail = pNew;
Vollversion:
LISTNODE* creat_FIFO_List(){
//step1+step2:定义变量+初始化
int num;
LISTNODE* pHead, pTrail, pNew;
pHead = NULL;
pTrail = NULL;
pNew = NULL;
printf("Input positive numbers, -1 to end.\n");
scanf("%d",&num);
while(num != -1){
//step3:创建节点,分配内存
//不管是pHead,pTrail,pNew,都是一个厂子出来的
pNew = malloc(sizeof(LISTNODE));
//step4:新节点插入链表
//得先判断内存申请成没成功
if(pNew != NULL){
pNew->data = num;
if(pHead == NULL){
//pHead从头到尾就只是占了个名字
pHead = pNew;
pTrail = pNew;
}else
{
pTrail->pNext = pNew;//追加
pTrail = pNew;
}
}
scanf("%d",&num);
}
pTrail->pNext = NULL;//设置链表结束标志
return pHead;
}
2. Knotenzugriff
• So greifen Sie auf die Knoten in der verknüpften Liste zu: Da der Speicher der Knoten in der verknüpften Liste dynamisch zugewiesen wird, kann nicht über den Namen darauf zugegriffen werden, sondern nur über die Adresse des Knotens.
•Die Adresse eines Knotens wird im Adressfeld seines Vorgängerknotens aufgezeichnet. Wenn Sie also den n-ten Knoten besuchen möchten, müssen Sie zuerst den n-1-ten Knoten besuchen und das Adressfeld des Knotens lesen. Wenn Sie ihn besuchen möchten Wenn Sie den n-1-ten Knoten besuchen, müssen Sie zuerst den n-2-ten Knoten besuchen und so weiter, bis Sie den ersten Knoten besuchen. Und der Zeiger zeigt auf den ersten Knoten headPtr
, sodass auf den ersten Knoten zugegriffen werden kann, sodass auch auf den zweiten Knoten und den dritten Knoten zugegriffen werden kann ...
void print_List(LISTNODE* pNew){
if(pNew = NULL)
printf("The list is empty\n");
else{
printf("The list is:\n");
while(pNew != NULL){
printf("%d-->",pNew->data);
pNew = pNew->pNext;
}
printf("NULL\n\n");
}
}
Hauptfunktion:
int main ()
{
LISTNODE* pHead;
pHead = creat_FIFO_List();
}
3. Dynamisches Hinzufügen und Löschen verknüpfter Listenknoten
Zunahme
if(pPre == NULL){
//插在链表首结点
pNew->pNext = pCurrent;
pHead = pNew;
}
else if(pCurrent = NULL)
{
//插在结尾
pPre->pNext = pNew;
pNew->pNext = NULL;
}
else
{
//插在中间
pPre->pNext = pNew;
pNew->pNext = pCurrent;
}
löschen
if(pCurrent != NULL){
if(pPre == NULL)
pHead = pCurrent->pNext;
else
pPre->pNext = pCurrent->pNext;
free(pCurrent);
}
• Das Einfügen und Löschen verknüpfter Listen ist hocheffizient und erreicht O(1). Für Daten, die nicht durchsucht werden müssen, sich aber häufig ändern und deren Obergrenze unvorhersehbar ist, wie z. B. Speicherpool, Prozessverwaltung des Betriebssystems, Trunk-Verwaltung des Netzwerkkommunikationsprotokollstapels usw., ist es absolut unmöglich, darauf zu verzichten Es.
• Die bemerkenswerteste Anwendung ist das Dateisystem. Wenn Sie die Festplatte formatieren, werden Sie zur Auswahl fat32
und ntfs
Formatierung aufgefordert. Tatsächlich können Sie hier die Größe und das Format des verknüpften Listenspeicherplatzes auswählen. Um die Systemeffizienz zu verbessern, müssen Sie manchmal eine Dateidefragmentierung durchführen, was bedeutet, dass die Daten einer Datei nicht unbedingt kontinuierlich gespeichert werden. Woher weiß das Betriebssystem also, diskontinuierliche Daten in einer Datei zu synthetisieren und Ihnen diese zur Verfügung zu stellen?
Grundlegende Funktionsweise einer verknüpften Liste
typedef struct ListNode{
int data;
struct ListNode* pNext;
}LISTNODE,*pLISTNODE;
Grundlegende Operationen für verknüpfte Listen:
Verknüpfte Listen erstellen, Knoten abrufen (finden), Knoten einfügen, löschen, Knoten ändern usw.
**(**1) Die Erstellung einer verknüpften Liste besteht tatsächlich aus einer Reihe von Knoteneinfügungsvorgängen, und die Zeitspanne kann lang oder kurz sein. Daher können wir die Einfügungsknoten-Operationsfunktion entwerfen, die für verschiedene Situationen geeignet ist, um die Erstellungsfunktion zu realisieren.
(2) Wenn eine verknüpfte Liste zum Implementieren einer First-In-First-Out-Warteschlange verwendet wird, müssen wir jeden neuen Knoten am Ende der verknüpften Liste einfügen, was als Tail-Insertion bezeichnet wird .
(3) Wenn eine verknüpfte Liste zum Implementieren eines Last-In-First-Out-Stapels verwendet wird, müssen wir jeden neuen Knoten in den Kopf der verknüpften Liste einfügen, was als Header-Einfügung bezeichnet wird .
(4) Wenn eine verknüpfte Liste verwendet wird, um eine nach Elementwerten sortierte lineare Liste zu implementieren, müssen wir die verknüpfte Liste durchlaufen, den ersten Knoten finden, der größer als der Datenwert des neuen Knotens ist, und ihn zwischen diesem und dem einfügen Vorgängerknoten, der als geordnete Einfügung bezeichnet wird .
Verknüpfte Liste mit leeren Knoten
Der erste Knoten speichert eigentlich keine Daten, sondern nur die Adresse des nächsten Knotens. Daher die verlinkte Listekann niemals leer sein. Daher kann die Beurteilungslogik des Programms vereinfacht werden: Beim Einfügen eines Knotens oder Löschen eines Knotens muss nicht beurteilt werden, ob der Vorgängerknoten leer ist.
Vorbereitung: Kopfzeiger und leerer Knoten:
/*创建一个带空节点的链表头*/
void create_ListHead(pLISTNODE* ppHead){
*ppHead = malloc(sizeof(LISTNODE));
if((*ppHead) != NULL){
(*ppHead)->pNext = NULL;
}
}
Hauptprogrammfragment:
int main (){
LISTNODE pHead = NULL;
creat_ListHead(&pHead);
if(pHead == NULL){
printf("List is not created.no memory avaliable\n");
exit(-1);
}...
}
//尾插
void append1(pLISTNODE,int);
void append2(pLISTNODE,int);
//头插
void appfront(pLISTNODE,int);
//中间插
void insert(pLISTNODE,int);
Schwanzstecker
Den ganzen Weg bis zum Endknoten durchqueren
Endpunktfunktionen:pTrail->pNext == NULL
Verbinden Sie den Schwanz:pTrail->pNext = pNew
void append1(pLISTNODE p, int val){
pLISTNODE pNew,pTrail;
//创造新节点
pNew = malloc(sizeof(pLISTNODE));
if(pNew != NULL){
//创造成功,,开始初始化
pNew->data = val;
pNew->pNext = NULL;
/*找到尾结点,记为pTrail*/
pTrail = p;
while(pTrail->pNext != NULL){
pTrail = pTrail->pNext;
}
/*接尾巴*/
pTrail->pNext = pNew;
}else
printf("Not inserted. No memory avaliable.\n")
}
Optimierung der Schwanzeinfügung
Um das einfache Anhängen neuer Knoten an den Endknoten der verknüpften Liste zu ermöglichen, kann ein Endzeiger so entworfen werden, dass er pTrail
auf den Endknoten der verknüpften Liste zeigt.
Anhängevorgang:
pTrail->pNext = pNew;
pTrail = pNew;
/*创建一个带空节点的表头*/
void create_ListHead(pLISTNODE* ppHead, pLISTNODE* ppTrail){
*ppHead = malloc(sizeof(pLISTNODE));
if((*ppHead) != NULL){
(*ppHead)->pNext = NULL;
*ppTrail = *ppHead;
}
}
/*把一个新值插入到链表尾,利用尾指针*/
void append2( pLISTNODE* ppTrail,int val){
pLISTNODE pNew;
pNew = malloc(sizeof(pLISTNODE));
if(pNew != NULL){
pNew->data = val;
pNew->pNext = NULL;
(*ppTrail)->pNext = pNew;
(*ppTrail) = pNew;
}
else
printf("not inserted, no memory avaliable\n")
}
Das Folgende ist falsch, der Parameter übergibt nur den Zeiger des Endknotens, warum nicht, wo nicht?
/*把一个新值插入到链表尾,利用尾指针*/
void append2( pLISTNODE pTrail,int val){
pLISTNODE pNew;
pNew = malloc(sizeof(pLISTNODE));
if(pNew != NULL){
pNew->data = val;
pTrail = pNew;
}
**Zusammenfassung:** Wenn Sie jemandem einen Wert zuweisen möchten, müssen Sie seinen Zeiger in der Funktion verwenden. Ebenso, wenn Sie den Wert in der Hauptfunktion ändern möchten:
Möchten Sie die Hauptfunktion ändern: | Es ist notwendig, den entsprechenden Zeiger an die Funktion zu übergeben |
---|---|
gemeinsame Variable | Zeiger der Ebene 1 |
Zeiger der Ebene 1 | sekundärer Zeiger |
sekundärer Zeiger | Drei Hinweise |
… | … |
Erweitertes Wissen: Wie bedient man die gesamte verknüpfte Liste mit nur einem Zeiger?
Option 1: Ringverknüpfte Liste:
Richten Sie den Endknoten der verknüpften Liste auf den leeren Knoten des Kopfes der verknüpften Liste und ändern Sie den Endzeiger in den Kopfzeiger, nämlich:pTrail->pNext = pHead
PS: Tatsächlich entspricht es einer Variablen
a = b
. Behalten Sie sie dann undb
werfen Sie sie wega
.Denken Sie daran: Fan sieht, dass die Funktion der Funktion den externen Zeiger direkt zuweist. Wenn ich beispielsweise
pTrail = pNext
diese Operation ausführen möchte, muss ich den sekundären Zeiger verwenden:(*ppTrail) = pNext;
Hier betonen wir die Änderung des Namens der externen Variablen (einschließlich). die Zeigervariable).
Option 2: Zirkuläre doppelt verknüpfte Liste
Erweitern Sie das Zeigerfeld des Knotens der verknüpften Liste, um den Zeiger des Vorgängerknotens einzuschließen, und lassen Sie den Endknoten der verknüpften Liste auf den leeren Knoten des Kopfs der verknüpften Liste zeigen, dh auf die bidirektionale zirkuläre verknüpfte Liste.
Auf diese Weise ist der Vorgängerknoten des Knotens, auf den der Kopfzeiger zeigt, der Endknoten, und der Endzeiger wird nicht mehr benötigt.
Stecker: Last in first out
Der neu hinzugefügte Knoten muss nach dem Kopf der verknüpften Liste/des leeren Knotens eingefügt werden.
pcurrent = pHead->pNext;
pHead->pNext = pNew;
pNew->pNext = pCurrent;