寫在前面:
? ? ? ? 在前面C語言的結構體學習中,我提及了鏈表的操作,?學習數據結構我認為還是需要對C語言的數組、函數、指針、結構體有一定的了解,不然對于結構體的代碼可能很難理解,特別是一些書籍上面用的還是偽代碼,就很難理解。所以我建議大家可以看看我前面C語言的篇章,其實在C語言的結構體這篇博客中我已經引入了數據結構中——鏈表的使用,只不過相對簡單,其動態鏈表對應的就是本章的單鏈表的一些內容,所以有了那篇博客,就能很好的順應到數據結構上來。
C語言——結構體-CSDN博客
一、線性表概述
? ? ? ? 線性結構包括:線性表、棧、隊列、串和數組;
? ? ? ? 線性結構的特點是:除了第一個元素沒有前驅,最后一個元素沒有后繼以外,其余的元素的都有前驅和后繼。
? ? ? ? 線性表是最常用的線性結構,由有限個特性相同的元素構成的序列稱為線性表;
線性表的特點:
1、存在唯一的元素被稱為“第一個”數據元素;
2、存在唯一的元素被稱為“最后一個”數據元素;
3、除第一個元素外,結構中每個元素都有一個前驅;
4、除最后一個元素外,結構中每一個元素都有一個后繼;
線性表按照存儲方式分為:順序存儲與鏈式存儲。?
二、順序表
線性表的順序存儲——順序表;
線性表的鏈式存儲——鏈表;?
順序表基本上就是數組,其邏輯上是相鄰的元素,其物理地址也是相鄰的。
2.1靜態初始化
? ? ? ? 這個靜態和動態如何區分呢?
? ? ? ? 在前面的C語言的結構體中,我們指出動態內存分配;
? ? ? ? 我們講,全局變量定義在內存的靜態存儲區,局部變量定義在內存中的動態存儲區;這個存儲區稱為棧區。
? ? ? ? 除此之外,內存還允許建立動態分配區域,用來存放臨時的數據,這些數據需要時隨時開辟,不需要時隨時釋放,這個區域稱為堆區。
? ? ? ? 對于內存的動態分配是通過系統提供的庫函數實現的,主要有malloc,calloc,free,recalloc函數。
????????也就是說,我們直接定義一個數組,那這個數組就是靜態的,我們利用動態庫函數定義一個數組,那這個數組就是動態存儲的。
#include <stdio.h>
#define MAXSIZE 10
typedef struct //創建一個結構體,其成員有兩個一個是data數組,用來存放數據,一個是變量length,用來存放數據長度。
{int data[MAXSIZE];int length;
}SeqList;//靜態初始化
void InitList(SeqList *L)
{for (int i = 0; i < MAXSIZE; i++){L->data[i] = 0;}L->length =0;
}int main()
{SeqList L;InitLink(&L);return 0;
}
?運行結果:創建靜態順序表,沒有什么輸出;只不過先定義了一個結構體,然后將數組作為一個成員存放在里面, 然后結構體里面還有一個變量,用來表示當前順序表的長度。
后面的增刪查改基本上和動態表是一樣的,所以就只介紹動態順序表的使用;?
?2.2動態初始化
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
typedef struct //創建一個結構體類型
{int * data;//指向動態分配數組的指針int Max;//順序表的最大容量int length;//順序表目前的長度
}SeqList;void InitList(SeqList * L)
{L->data = (int *)malloc(MAXSIZE*sizeof(int));L->length = 0;L->Max = MAXSIZE;
}int main()
{SeqList L;InitList(&L);
}
????????運行結果與上面一樣;我們需要注意的是,在使用動態順序表初始化的時候,我們并沒有直接定義數組,而是利用malloc函數動態的創建了一塊內存地址,這個地址的大小是由規則的小空間組成,所存儲的數據性質是一樣的,
2.2.1增
? ? ? ? 增也可以說是插入,就是在原有的線性表基礎上再插入一個元素;需要注意的是:
1、插入一次只能插入一個元素;
2、插入元素的位置要合理。即下圖:
3、插入元素前,順序表的大小要小于最大容量;
????????上圖表中,最大長度和位置為上面數組12345678910;數組的編號是從a[0]開始,那也就需要注意,第i個位置的元素對應的是數組的a[i-1];
????????當length的長度為0,也就是空表的時候,我們只能往1的那位置插入。?
????????當length的長度為1,我們只能往1和2的那位置插入。往1插入23就會移到后面2的位置,1中為新插入的數據。
? ? ? ? 所以插入元素的位置要合理——i>1 以及?i<=length + 1;插入位置不能為0,沒有0這個位置,插入位置不能大于長度+1,不然就不連續,長度+1的位置就會空出來。
? ? ? ? 此外,插入位置之前,lengh的長度要小于最大容量,因為插入后length+1最多只能和最大容量一樣大,要不然就會溢出。
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
typedef struct //創建一個結構體
{int * data;//指向動態分配數組的指針int Max;//順序表的最大容量int length;//順序表目前的長度
}SeqList;void InitList(SeqList * L)
{L->data = (int *)malloc(MAXSIZE*sizeof(int));L->length = 0;L->Max = MAXSIZE;
}int InsertList(SeqList * L, int i,int e)
{if (i<1 || i> L->length + 1){printf("插入位置不合法\n");return 0;}if (L->length >= L->Max){printf("當前存儲空間已滿\n");}for (int j = L->length; j >= i; j--){L->data[j] = L->data[j - 1];}L->data[i - 1] = e;L->length++;return 1;
}
void PrintfList(SeqList L)
{for (int i = 1; i <= L.length; i++){printf("%d->", L.data[i - 1]);}printf("NULL\n");}int main()
{SeqList L;InitList(&L);PrintfList(L);InsertList(&L, 1, 1);//在第一個位置插入數據1;PrintfList(L);InsertList(&L, 1, 2);//在第一個位置插入數據2;PrintfList(L);InsertList(&L, 2, 3);//在第二個位置插入數據3;PrintfList(L);InsertList(&L, 4, 4);//在第四個位置插入數據4;PrintfList(L);InsertList(&L, 6, 1);//在第六個位置插入數據1;PrintfList(L);}
2.2.2刪
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
typedef struct //創建一個結構體
{int * data;//指向動態分配數組的指針int Max;//順序表的最大容量int length;//順序表目前的長度
}SeqList;void InitList(SeqList * L)
{L->data = (int *)malloc(MAXSIZE*sizeof(int));L->length = 0;L->Max = MAXSIZE;
}int InsertList(SeqList * L, int i,int e)
{if (i<1 || i> L->length + 1){printf("插入位置不合法\n");return 0;}if (L->length >= L->Max){printf("當前存儲空間已滿\n");}for (int j = L->length; j >= i; j--){L->data[j] = L->data[j - 1];}L->data[i - 1] = e;L->length++;return 1;
}int DeleteLink(SeqList * L, int i)
{if (i<1 || i> L->length + 1){printf("刪除位置不合法\n");return 0;}if (L->length <= 0){printf("當前存儲空間為0\n");}int e = L->data[i - 1];for (int j = i; j < L->length; j++){L->data[j-1] = L->data[j];}L->length--;return e;}void PrintfList(SeqList L)
{for (int i = 1; i <= L.length; i++){printf("%d->", L.data[i - 1]);}printf("NULL\n");}int main()
{int e;SeqList L;InitList(&L);InsertList(&L, 1, 1);InsertList(&L, 1, 2);InsertList(&L, 2, 3);InsertList(&L, 4, 4);PrintfList(L);e=DeleteLink(&L, 2);printf("刪除的元素的值為:%d\n", e);PrintfList(L);
}
運行結果:?
2.2.3查
????????查找分為按位查找與按值查找,按位查找直接利用L->data[i-1]就可以把第i位的值查找出來,沒有多大意義,我們重點來說一下按位查找;
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
typedef struct //創建一個結構體
{int * data;//指向動態分配數組的指針int Max;//順序表的最大容量int length;//順序表目前的長度
}SeqList;void InitList(SeqList * L)
{L->data = (int *)malloc(MAXSIZE*sizeof(int));L->length = 0;L->Max = MAXSIZE;
}int InsertList(SeqList * L, int i,int e)
{if (i<1 || i> L->length + 1){printf("插入位置不合法\n");return 0;}if (L->length >= L->Max){printf("當前存儲空間已滿\n");}for (int j = L->length; j >= i; j--){L->data[j] = L->data[j - 1];}L->data[i - 1] = e;L->length++;return 1;
}int DeleteLink(SeqList * L, int i)
{if (i<1 || i> L->length + 1){printf("刪除位置不合法\n");return 0;}if (L->length <= 0){printf("當前存儲空間為0\n");}int e = L->data[i - 1];for (int j = i; j < L->length; j++){L->data[j-1] = L->data[j];}L->length--;return e;}int ReserchList(SeqList L, int e)
{for (int i = 0; i < L.length; i++){if (e = L.data[i]){return i + 1;}}printf("查找失敗,沒有該元素的值;\n");return 0;
}void PrintfList(SeqList L)
{for (int i = 1; i <= L.length; i++){printf("%d->", L.data[i - 1]);}printf("NULL\n");}int main()
{int e;SeqList L;InitList(&L);InsertList(&L, 1, 1);InsertList(&L, 1, 2);InsertList(&L, 2, 3);InsertList(&L, 4, 4);PrintfList(L);e = ReserchList(L, 2);printf("查找后,該值的位置為%d\n", e);}
2.2.4擴容
? 擴容是只針對動態內存的,擴容的步驟:
1、生成指向原來順序表的存儲空間的指針;
2、為順序表開辟新的一塊更大的空間
3、數據轉移;
4、修改順序表的最大長度;
5、用free釋放掉原有的空間。
int IncreatSize(SeqList * L, int len)
{
?? ?int * p = L->data;?? ?L->data = (int*)malloc((L->Max + len)*sizeof(int));
?? ?for (int i = 0; i < L->length; i++)
?? ?{
?? ??? ?L->data[i] = p[i];
?? ?}
?? ?L->Max += len;
?? ?free(p);
?? ?return 0;
}
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
typedef struct //創建一個結構體
{int * data;//指向動態分配數組的指針int Max;//順序表的最大容量int length;//順序表目前的長度
}SeqList;void InitList(SeqList * L)
{L->data = (int *)malloc(MAXSIZE*sizeof(int));L->length = 0;L->Max = MAXSIZE;
}int InsertList(SeqList * L, int i,int e)
{if (i<1 || i> L->length + 1){printf("插入位置不合法\n");return 0;}if (L->length >= L->Max){printf("當前存儲空間已滿\n");}for (int j = L->length; j >= i; j--){L->data[j] = L->data[j - 1];}L->data[i - 1] = e;L->length++;return 1;
}int DeleteLink(SeqList * L, int i)
{if (i<1 || i> L->length + 1){printf("刪除位置不合法\n");return 0;}if (L->length <= 0){printf("當前存儲空間為0\n");}int e = L->data[i - 1];for (int j = i; j < L->length; j++){L->data[j-1] = L->data[j];}L->length--;return e;}int ReserchList(SeqList L, int e)
{for (int i = 0; i < L.length; i++){if (e = L.data[i]){return i + 1;}}printf("查找失敗,沒有該元素的值;\n");return 0;
}void PrintfList(SeqList L)
{for (int i = 1; i <= L.length; i++){printf("%d->", L.data[i - 1]);}printf("NULL\n");}//擴容:
/*
1、生成指向原來順序表的存儲空間的指針;
2、為順序表開辟新的一塊更大的空間
3、數據轉移;
4、修改順序表的最大長度;
5、用free釋放掉原有的空間。
*/
int IncreatSize(SeqList * L, int len)
{int * p = L->data;L->data = (int*)malloc((L->Max + len)*sizeof(int));for (int i = 0; i < L->length; i++){L->data[i] = p[i];}L->Max += len;free(p);return 0;
}
三、單鏈表
3.1概述
? ? ? ? 鏈表:邏輯上相鄰的數據元素,其物理地址不一定相鄰;用一組任意的存儲單元存儲線性表的元素(這組存儲單元可以是連續的,也可以是不連續的。)因此,為了表示每個數據元素與前后元素的關系,對于某個元素來說,存儲的不僅是本身的信息,還需要指定下一個元素的信息;
? ? ? ? 這種由兩部分組成的數據元素的存儲,稱為結點;它包括兩個域:
????????存儲數據元素信息的區域稱為數據域;
????????存儲后繼元素位置的區域稱為指針域;
? ? ? ? 由于線性表的鏈式存儲,每個節點中只包含一個指針域,故稱為線性鏈表或者單鏈表;
一般情況下,為了處理方便,在單鏈表的第一個節點之前附設一個節點,稱之為頭結點。如圖紅色框中的結點;
?首元結點:指鏈表中存儲第一個數據元素的結點,上圖中,數據區域為1的結點。
?頭結點:是在首元結點之前附設的一個節點,其指針域指向首元結點,頭結點的數據域不存儲任何信息,也可以存儲一些附加信息。例如:如果當數據域的元素為整數型時,頭結點的數據域可存放該線性表的長度。
?頭指針:指向鏈表的第一個結點的指針,若鏈表設有頭結點,則頭指針所指結點為頭結點,如果不設頭結點,頭指針指向首元結點。
3.2單鏈表
?3.2.1初始化
在初始化之前我們先創建鏈表所需要的結點——即定義結構體類型:
typedef struct Node//定義一個結構體,并用typedef進行替換,即struct Node——Node;
{int data; //數據域,本次采用整形;struct Node * next;//指針域,指針類型為結構體指針;
}Node;
初始化單鏈表:
? ? ? ? 生成一個新的節點,用頭指針指向頭結點。頭結點的指針域置空;
Node * InitList() //定義一個初始化函數,其返回值為結構體指針,即將構建的鏈表首地址返回;
{Node * head;//定義一個頭指針;head= (Node *)malloc(sizeof(Node));//創建一個節點,并讓頭指針指向該節點,該節點也就是頭結點;head->data = 0;//頭結點的數據域為0;head->next = NULL;//頭結點的指針域為空;return head;//返回頭結點的地址
}
3.2.2插入?
前插法:
? ? ? ? 前插法是將新的結點逐個插入鏈表的頭部(頭結點的后面),每次申請一個新的節點,將其進行插入。
void HeadInsert(Node * L,int data)//定義一個前插函數,函數的參數分別為,鏈表的地址,以及新插入的元素值;
{Node * node = (Node *) malloc(sizeof(Node));//構建一個新的結點,然后返回其地址;node->data = data;//將傳入的數據存放在新節點的數據域;node->next = L->next;//將原來的頭結點的指針域的值,存放在新節點的指針域中;L->next = node;//將新節點的地址存放在頭指針的指針域;L->data++;//頭結點的數值域的數值加1,表示鏈表增加一個長度;
}
后插法:
? ? ? ? 后插法是通過將新節點逐個插入到鏈表的尾部,同前插法相同,每次申請一個新結點,將數值進行存放,不同的是,為了使新結點能夠插入到末尾,需要增加一個尾指針,尾指針指向鏈表的尾結點;
void TailInsert(Node * L, int data)//定義一個后插函數,函數的參數分別為,鏈表的地址,以及新插入的元素值;
{Node * node=L;//定義一個指針,類型為結構體指針;int i ;for (i = 0; i < L->data; i++)//將指針指向尾結點;{node = node->next;}Node * n = (Node *) malloc(sizeof(Node));//創建一個新的結點,將創建的結點的地址先放在n中;node->next = n;//將新結點的地址存放在尾結點的指針域;n->data = data;//將新元素的數據存放在新結點的數據域;n->next = NULL;//新結點重新作為尾結點,其指針域為空;L->data++;//頭結點的數值域數值加1;表示鏈表的長度+1;
}
3.2.3刪除?
? ? ? ? ?刪除指定位置的結點,首先應該找到該位置的前驅結點,在單鏈表刪除之前應將前驅結點的指針域的地址改為其后繼結點的地址。
int DeleteList(Node * L, int data)//定義一個刪除函數,函數的參數分別為,鏈表的地址,以及刪除的元素值;
{Node * p1 = L;//定義一個指針,類型為結構體指針,準備用來表指向前驅結點;Node * p2 = L->next;//定義一個指針,類型為結構體指針,準備用來表指向刪除的結點;while (p2)//逐個判斷其數值是否與鏈表中的一直,直到每一個對比完。{if (p2->data == data)//判斷是不是要刪除的元素{ //如果是p1->next = p2->next;//將該節點的指針域的值放在前驅結點的指針域中;free(p2);//釋放該節點的空間L->data--;//鏈表的長度減1;return 1;//刪除成功,返回1;} //如果不是p1 = p2; //依次向后移到下一個結點;p2 = p2->next;}return 0;//沒有要刪除的元素,返回0;
}
2.3.4打印?
void PrintList(Node * L)//定義一個打印函數,函數的參數為,鏈表的地址
{Node * node = L->next;//定義一個指針,類型為結構體指針,用來進行數據的移動;while (node)//當該結點的指針域為空時,表示打印結束;{printf("%d ", node->data);//打印結點的數據node = node->next;//指向下一個結點;}printf("\n");
}
2.3.5案例?
? ? ? ? 創建一個新鏈表,利用前插插入5個數,利用后插插入5個數,打印出來鏈表,然后刪除其中一些結點,再打印出來;
#include <stdio.h>
#include <stdlib.h>//定義鏈表結構體節點typedef struct Node//定義一個結構體,并用typedef進行替換,即struct Node——Node;
{int data; //數據域,本次采用整形;struct Node * next;//指針域,指針類型為結構體指針;
}Node;//初始化鏈表Node * InitList() //定義一個初始化函數,其返回值為結構體指針,即將構建的鏈表首地址返回;
{Node * head;//定義一個頭指針;head= (Node *)malloc(sizeof(Node));//創建一個節點,并讓頭指針指向該節點,該節點也就是頭結點;head->data = 0;//頭結點的數據域為0;head->next = NULL;//頭結點的指針域為空;return head;//返回頭結點的地址
}//鏈表頭插:
void HeadInsert(Node * L,int data)//定義一個前插函數,函數的參數分別為,鏈表的地址,以及新插入的元素值;
{Node * node = (Node *) malloc(sizeof(Node));//構建一個新的結點,然后返回其地址;node->data = data;//將傳入的數據存放在新節點的數據域;node->next = L->next;//將原來的頭結點的指針域的值,存放在新節點的指針域中;L->next = node;//將新節點的地址存放在頭指針的指針域;L->data++;//頭結點的數值域的數值加1,表示鏈表增加一個長度;
}//鏈表尾插;
void TailInsert(Node * L, int data)//定義一個后插函數,函數的參數分別為,鏈表的地址,以及新插入的元素值;
{Node * node=L;//定義一個指針,類型為結構體指針;int i ;for (i = 0; i < L->data; i++)//將指針指向尾結點;{node = node->next;}Node * n = (Node *) malloc(sizeof(Node));//創建一個新的結點,將創建的結點的地址先放在n中;node->next = n;//將新結點的地址存放在尾結點的指針域;n->data = data;//將新元素的數據存放在新結點的數據域;n->next = NULL;//新結點重新作為尾結點,其指針域為空;L->data++;//頭結點的數值域數值加1;表示鏈表的長度+1;
}//鏈表刪除int DeleteList(Node * L, int data)//定義一個刪除函數,函數的參數分別為,鏈表的地址,以及刪除的元素值;
{Node * p1 = L;//定義一個指針,類型為結構體指針,準備用來表指向前驅結點;Node * p2 = L->next;//定義一個指針,類型為結構體指針,準備用來表指向刪除的結點;while (p2)//逐個判斷其數值是否與鏈表中的一直,直到每一個對比完。{if (p2->data == data)//判斷是不是要刪除的元素{ //如果是p1->next = p2->next;//將該節點的指針域的值放在前驅結點的指針域中;free(p2);//釋放該節點的空間L->data--;//鏈表的長度減1;return 1;//刪除成功,返回1;} //如果不是p1 = p2; //依次向后移到下一個結點;p2 = p2->next;}return 0;//沒有要刪除的元素,返回0;
}//鏈表打印void PrintList(Node * L)//定義一個打印函數,函數的參數為,鏈表的地址
{Node * node = L->next;//定義一個指針,類型為結構體指針,用來進行數據的移動;while (node)//當該結點的指針域為空時,表示打印結束;{printf("%d ", node->data);//打印結點的數據node = node->next;//指向下一個結點;}printf("\n");
}int main()
{Node * L= InitList();HeadInsert(L, 1);HeadInsert(L, 2);HeadInsert(L, 3);HeadInsert(L, 4);HeadInsert(L, 5);TailInsert(L, 6);TailInsert(L, 7);TailInsert(L, 8);TailInsert(L, 9);TailInsert(L, 10);PrintList(L);int ret =DeleteList(L, 1);if (ret == 1){printf("sucess delete\n");}else{printf("fail delete\n");}PrintList(L);return 0;
}
運行結果:?
3.3單循環鏈表
????????循環鏈表是另一種形式的鏈式存儲結構,其特點是表中的追后一個節點的指針域指向頭結點,整個鏈表形成一個環,由此,鏈表的任何一個節點出發均可以找到表中的其他節點。
單循環鏈表的操作和單鏈表基本一致,差別進在于:當鏈表遍歷時,判別當前指針P是指向表尾結點的終止條件不同。
單鏈表:判別條件為P!=NULL;或者P->next!=NULL;?
單循環鏈表:判別條件為P!=L;或者P->next!=L;
案例:
#include <stdio.h>
#include <stdlib.h>#define True 1
#define False 0typedef struct Node
{ int data;struct Node * next;
}Node;Node * Init_Link()
{Node* head = (Node *)malloc(sizeof(Node));head->data = 0;head->next = head;return head;
}void HeadInsert(Node * L, int data)
{Node * n = (Node *)malloc(sizeof(Node));n->data = data;n->next = L->next;L->next = n;L->data++;
}
void TailInsert(Node * L, int data)
{Node * n = (Node *)malloc(sizeof(Node));Node * b = L->next;while (b->next!= L){b = b->next;}b->next = n;n->data = data;n->next = L;L->data++;
}void PrintLink(Node * L)
{Node * L1 = L->next;while (L1 != L){printf("%d->", L1->data);L1 = L1->next;}printf("NULL\n");
}int DeleteLink(Node * L, int data)
{Node * n1 = L;Node * n2 = L->next;while (n2 != L){if (n2->data == data){n1->next = n2->next;free(n2);L->data--;return True;}n1 = n2;n2 = n2->next;}return False;
}int main()
{Node* node = Init_Link();HeadInsert(node, 1);HeadInsert(node, 2);HeadInsert(node, 3);HeadInsert(node, 4);HeadInsert(node, 5);TailInsert(node, 6);TailInsert(node, 7);TailInsert(node, 8);TailInsert(node, 9);TailInsert(node, 10);PrintLink(node);DeleteLink(node, 5);DeleteLink(node, 6);PrintLink(node);return 0;
}
運行結果:?
四、雙向鏈表?
4.1雙向鏈表
? ? ? ? 以上討論的鏈式存儲結構的節點只有一個指示直接后繼的指針域,也就是說從某個節點出發只能順指針向后進行查找。若要尋查結點的直接前驅,則必須要從表頭指針出發。
? ? ? ? 為了克服單鏈表這種單向性的缺點,可以利用雙向鏈表。
? ? ? ? 顧名思義,雙向鏈表的節點由兩個指針域,一個指向直接后繼,一個指向直接前驅。結點的結構如下圖所示:
#include <stdio.h>
#include <stdlib.h>#define True 1
#define False 2typedef struct Node
{int data;struct Node * pre;struct Node * next;
}Node;Node * InitLink()
{Node * head = (Node*)malloc(sizeof(Node));head->data = 0 ;head->next = NULL;head->pre = NULL;
}void HeadInsert(Node * L, int data)
{Node * n = (Node *)malloc(sizeof(Node));n->data = data;if (L->next == NULL){n->pre = L;n->next = L->next;L->next = n;}else{n->next = L->next;n->pre = L;L->next->pre = n;L->next = n;}L->data++;
}void TailInsert(Node * L,int data)
{Node * n = (Node *)malloc(sizeof(Node));n->data = data;Node * n1=L;while (n1->next ){n1 = n1->next;}n->pre = n1;n->next = n1->next;n1->next = n;L->data++;
}
int DeleteLink(Node * L, int data)
{Node * n = L;while (n){if (n->data == data){n->pre->next = n->next;if (n->next){n->next->pre = n->pre;}free(n);L->data--;return True;}n = n->next;}return False;}void PrintLint(Node * L)
{Node * n = L->next;while (n){printf("%d->", n->data);n = n->next;}printf("NULL\n");}int main(){Node *L = InitLink();HeadInsert(L, 1);HeadInsert(L, 2);HeadInsert(L, 3);HeadInsert(L, 4);HeadInsert(L, 5);TailInsert(L, 6);TailInsert(L, 7);TailInsert(L, 8);TailInsert(L, 9);TailInsert(L, 10);PrintLint(L);DeleteLink(L, 5);DeleteLink(L, 6);DeleteLink(L, 10);PrintLint(L);return 0;}
運行結果:?
?4.2雙循環鏈表
???循環鏈表是另一種形式的鏈式存儲結構,其特點是表中的追后一個節點的指針域指向頭結點,整個鏈表形成一個環,由此,鏈表的任何一個節點出發均可以找到表中的其他節點。
? ? ? ? 雙向循環鏈表的是結合雙鏈表和循環鏈表的特點,其基本的算法與前文相似,需要注意的是插入和刪除時有很大的不用,在雙鏈表種需要需改多個指針。
#include <stdio.h>
#include <stdlib.h>#define True 1
#define False 0typedef struct Node
{int data;struct Node * pre;struct Node * next;
}Node;Node * InitLink()
{Node * head = (Node*)malloc(sizeof(Node));head->data = 0;head->next =head;head->pre = head;return head;
}void HeadInter(Node* L, int data)
{Node* node = (Node*)malloc(sizeof(Node));node->data = data;node->next = L->next;node->pre = L;L->next->pre = node;L->next = node;L->data++;
}
void TailInter(Node * L, int data)
{Node * node = L;while (node->next != L){ node = node->next;}Node * n = (Node *)malloc(sizeof(Node));n->data = data;n->pre = node;n->next = L;L->pre = n;node->next = n;L->data++;
}
int DeleteLink(Node* L, int data)
{Node * node = L->next;while (node!= L){if (node->data == data){node->pre->next = node->next;node->next->pre = node->pre;free(node);L->data--;return True;}node = node->next;}return False;
}void PrintLink(Node *L)
{Node* node = L->next;while (node!=L){printf("%d->", node->data);node = node->next;}printf("NULL\n");
}int main()
{Node * L = InitLink();HeadInter(L, 1);HeadInter(L, 2);HeadInter(L, 3);HeadInter(L, 4);HeadInter(L, 5);TailInter(L, 6);TailInter(L, 7);TailInter(L, 8);TailInter(L, 9);TailInter(L, 10);PrintLink(L);DeleteLink(L,7);DeleteLink(L,6);DeleteLink(L,5);DeleteLink(L,1);PrintLink(L);return 0;
}
運行結果:
需要注意的時,在指針進行交換的時候,一定需要注意順序;例如:
?在頭插法中:
void HeadInter(Node* L, int data)
{
?? ?Node* node = (Node*)malloc(sizeof(Node));
?? ?node->data = data;
?? ?node->next = L->next;
?? ?node->pre = L;
?? ?L->next->pre = node;
?? ?L->next = node;
?? ?L->data++;
}????????如果上述兩個代碼的順序發生錯誤,就會在刪除頭插數據時出現錯誤,因為其連接發生錯誤。
????????當增加指針時,一定注意,先修改新插的節點,再修改后繼節點,最后再修改前驅節點。不然就會發生錯誤。
總結:
? ? ? ? 本節介紹了線性表的順序存儲——順序表,以及鏈式存儲——鏈表,我們發現,存取元素順序表更方便,插入和刪除元素,鏈表更方便。線性變也是數據結構的基礎,能夠很好的將C語言同數據結構后面的內容聯系起來。大家可以多多練習;
創作不易,感謝大家多多點贊支持!