基礎---單鏈表的實現(帶頭)
阿新 • • 發佈:2021-08-23
基本術語
- 結點:單鏈表是一種鏈式存取的資料結構,連結串列中的資料是以結點來表示的,每個結點的構成:元素(資料元素的映象) + 指標(指示後繼元素儲存位置),元素就是儲存資料的儲存單元,指標就是連線每個結點的地址資料。
單鏈表的實現(帶頭連結串列)
定義一個結點
//使用結構體來定義一個結點。注: 只要不是關鍵字,那就都是變數名,不必因為看著太規範而懷疑它為關鍵字。 typedef struct Node{ int data;//儲存資料,DATA部分。 struct Node *next;//儲存指標,NEXT部分。 }Node,*LinkedList;//Node是結構體struct Node的別名,*LinkedList是一個指向struct Node的指標。
初始化結點
LinkedList listinit(){ Node *L; L=(Node*)malloc(sizeof(Node)); //開闢空間 if(L==NULL){ //判斷是否開闢空間失敗,這一步很有必要 printf("申請空間失敗"); //exit(0); //開闢空間失敗可以考慮直接結束程式 } L->next=NULL; //指標指向空 } //檢查很有必要,但下面那幾個建立單鏈表的似乎都沒檢查。 //(沒試過)下面那幾個初始結點的操作,似乎可以直接用這個函式代替掉。這個函式裡面寫好檢查開闢成功與否的方法,再初始化結點時更加方便也更加安全。
建立單鏈表(重點)
分為兩種建立方式:(好像還有有頭結點的和沒頭結點的,暫時沒看)
//頭插入法建立單鏈表 LinkedList LinkedListCreatH() { Node *L; L = (Node *)malloc(sizeof(Node)); //申請頭結點空間 L->next = NULL; //初始化一個空連結串列 int x; //x為連結串列資料域中的資料 while(scanf("%d",&x) != EOF) { Node *p; p = (Node *)malloc(sizeof(Node)); //申請新的結點 p->data = x; //結點資料域賦值 p->next = L->next; //將結點插入到表頭L-->|2|-->|1|-->NULL L->next = p; } return L; /* 一步一步的分析: 1.聲明瞭一個指標變數L,這個指標變數儲存了一個結構體的地址。而這個結構體裡還可以有一個指標,這個指標(NEXT)不要和這個指標變數本身儲存的指標混淆。也就是L儲存了一個地址,L->next儲存了另外的地址,L的地址指向這個結構體,L->next的指標指向另外的結構體。 長話短說就是L是一個地址,*L才是一個結構體。 //第一輪迴圈 2.開闢了一個記憶體空間,目標結構體算是正式誕生了,這個結構體的next指向NULL。 3.聲明瞭一個指標變數p,開闢結構體記憶體空間。 4.*p的data存上資料x,*p的next存上*L的next 5.*L的next存上當前結構體p的地址(不要忘了p是一個指標變數,存的是*p的地址) //第二輪迴圈 2.3.聲明瞭一個新的指標變數p,這個指標變數和前面p那個毫無關係。 4.新p的data存上資料,新p的next存上*L的next, 由第一輪迴圈我們可以知道此時的*L的next儲存的是第一輪那個*p的地址,這樣第二個p就順理成章地指向了第一個p。 5.*L的next存上當前結構體p的地址 //第三輪迴圈 。。。 */
頭插法圖例:
//尾插法建立單鏈表
LinkedList LinkedListCreatT() {
Node *L;
L = (Node *)malloc(sizeof(Node)); //申請頭結點空間
L->next = NULL; //初始化一個空連結串列
Node *r;
r = L; //r始終指向終端結點,開始時指向頭結點
int x; //x為連結串列資料域中的資料
while(scanf("%d",&x) != EOF) {
Node *p;
p = (Node *)malloc(sizeof(Node)); //申請新的結點
p->data = x; //結點資料域賦值
r->next = p; //將結點插入到表頭L-->|1|-->|2|-->NULL
r = p;
}
r->next = NULL;
return L;
}
/*
這些分析預設讀者理解了頭插法,所以採取簡寫的形式。
1.建立一個頭結點*L
2.建立一個容器r用於後面的移動。
//第一輪迴圈
3.建立結構體*p
4.存資料
5.看似是*r的next其實是*L的next,事實上兩者就是一個東西,因為上文把L指標所儲存的地址賦值給了r,L和r指向的是一個東西。
6.像步驟5一樣,讓r和p指向同一個東西---第一個*p
//第二輪迴圈
3.建立新結構體*p
4.存資料
5.看似是*r的next其實是步驟一中*p的next,事實上兩者就是一個東西,因為上文把第一個p指標所儲存的地址賦值給了r,第一個p和r指向的是一個東西。
6.像步驟5一樣,讓r和p指向同一個東西---第二個*p
//第三輪迴圈
3.建立新結構體*p
4.存資料
5.看似是*r的next其實是步驟二中*p的next,事實上兩者就是一個東西,因為上文把第二個p指標所儲存的地址賦值給了r,第二個p和r指向的是一個東西。
6.像步驟5一樣,讓r和p指向同一個東西---第三個*p
//第四輪迴圈
。。。
7.迴圈完畢後,很容易發現,最後一個*p的next是沒有值的,我們需要手動新增一個NULL給最後的*p
*/
尾插法圖例:
遍歷單鏈表
//遍歷輸出單鏈表
void printList(LinkedList L){
Node *p=L->next;
int i=0;
while(p){
printf("第%d個元素的值為:%d\n",++i,p->data);
p=p->next;
}
}
//p只有為NULL的時候,while迴圈才能被跳出。
//沒有破壞原連結串列,因為p是一個新定義的指標,沒有涉及到原連結串列的指標。
修改單鏈表
//連結串列內容的修改,再連結串列中修改值為x的元素變為為k。
LinkedList LinkedListReplace(LinkedList L,int x,int k) {
Node *p=L->next;
int i=0;
while(p){
if(p->data==x){
p->data=k;
}
p=p->next;
}
return L;
}
插入新結點
//單鏈表的插入,在連結串列的第i個位置插入x的元素
LinkedList LinkedListInsert(LinkedList L,int i,int x) {
Node *pre; //pre為前驅結點
pre = L;
int tempi = 0;
for (tempi = 1; tempi < i; tempi++) {
pre = pre->next; //查詢第i個位置的前驅結點
}
Node *p; //插入的結點為p
p = (Node *)malloc(sizeof(Node));
p->data = x;
p->next = pre->next; //將前置結點的next給新結點,使其指向原第i個結點。
pre->next = p; //使前置結點指向新結點。
return L;
}
刪除結點
//單鏈表的刪除,在連結串列中刪除值為x的元素
LinkedList LinkedListDelete(LinkedList L,int x) {
Node *p,*pre; //pre為前驅結點,p為查詢的結點。
p = L->next;
while(p->data != x) { //查詢值為x的元素
pre = p; //
p = p->next; //這兩步操作完成後,pre始終落後p一個結點的身位。
}
pre->next = p->next; //刪除操作,將其前驅next指向其後繼。
free(p);
return L;
}
//當迴圈到目標結點時,也就是while在退出迴圈的前一次迴圈中,pre通過迴圈的第一步指向目標刪除節點的前一個結點,p通過迴圈的第二步,指向了目標刪除節點。
//跳出迴圈後,讓前驅節點指向後繼結點,從而實現刪除的效果。
//這個被刪除的結點需要用free()函式來釋放記憶體空間。
完整的單鏈表程式碼
#include <stdio.h>
#include <stdlib.h>
//定義結點型別
typedef struct Node {
int data; //資料型別,你可以把int型的data換成任意資料型別,包括結構體struct等複合型別
struct Node *next; //單鏈表的指標域
} Node,*LinkedList;
//單鏈表的初始化
LinkedList LinkedListInit() {
Node *L;
L = (Node *)malloc(sizeof(Node)); //申請結點空間
if(L==NULL){ //判斷申請空間是否失敗
exit(0); //如果失敗則退出程式
}
L->next = NULL; //將next設定為NULL,初始長度為0的單鏈表
return L;
}
//單鏈表的建立1,頭插法建立單鏈表
LinkedList LinkedListCreatH() {
Node *L;
L = (Node *)malloc(sizeof(Node)); //申請頭結點空間
L->next = NULL; //初始化一個空連結串列
int x; //x為連結串列資料域中的資料
while(scanf("%d",&x) != EOF) {
Node *p;
p = (Node *)malloc(sizeof(Node)); //申請新的結點
p->data = x; //結點資料域賦值
p->next = L->next; //將結點插入到表頭L-->|2|-->|1|-->NULL
L->next = p;
}
return L;
}
//單鏈表的建立2,尾插法建立單鏈表
LinkedList LinkedListCreatT() {
Node *L;
L = (Node *)malloc(sizeof(Node)); //申請頭結點空間
L->next = NULL; //初始化一個空連結串列
Node *r;
r = L; //r始終指向終端結點,開始時指向頭結點
int x; //x為連結串列資料域中的資料
while(scanf("%d",&x) != EOF) {
Node *p;
p = (Node *)malloc(sizeof(Node)); //申請新的結點
p->data = x; //結點資料域賦值
r->next = p; //將結點插入到表頭L-->|1|-->|2|-->NULL
r = p;
}
r->next = NULL;
return L;
}
//單鏈表的插入,在連結串列的第i個位置插入x的元素
LinkedList LinkedListInsert(LinkedList L,int i,int x) {
Node *pre; //pre為前驅結點
pre = L;
int tempi = 0;
for (tempi = 1; tempi < i; tempi++) {
pre = pre->next; //查詢第i個位置的前驅結點
}
Node *p; //插入的結點為p
p = (Node *)malloc(sizeof(Node));
p->data = x;
p->next = pre->next;
pre->next = p;
return L;
}
//單鏈表的刪除,在連結串列中刪除值為x的元素
LinkedList LinkedListDelete(LinkedList L,int x) {
Node *p,*pre; //pre為前驅結點,p為查詢的結點。
p = L->next;
while(p->data != x) { //查詢值為x的元素
pre = p;
p = p->next;
}
pre->next = p->next; //刪除操作,將其前驅next指向其後繼。
free(p);
return L;
}
//連結串列內容的修改,再連結串列中修改值為x的元素變為為k。
LinkedList LinkedListReplace(LinkedList L,int x,int k) {
Node *p=L->next;
int i=0;
while(p){
if(p->data==x){
p->data=k;
}
p=p->next;
}
return L;
}
//便利輸出單鏈表
void printList(LinkedList L){
Node *p=L->next;
int i=0;
while(p){
printf("第%d個元素的值為:%d\n",++i,p->data);
p=p->next;
}
}
int main() {
//建立
LinkedList list;
printf("請輸入單鏈表的資料:以EOF結尾\n");
printf("以EOF結尾就是在末尾ctrl+z一下(windows系統下)\n");
list = LinkedListCreatT();
//list=LinkedListCreatT();
printList(list);
//插入
int i;
int x;
printf("請輸入插入資料的位置:");
scanf("%d",&i);
printf("請輸入插入資料的值:");
scanf("%d",&x);
LinkedListInsert(list,i,x);
printList(list);
//修改
printf("請輸入修改的資料:");
scanf("%d",&i);
printf("請輸入修改後的值:");
scanf("%d",&x);
LinkedListReplace(list,i,x);
printList(list);
//刪除
printf("請輸入要刪除的元素的值:");
scanf("%d",&x);
LinkedListDelete(list,x);
printList(list);
return 0;
}