五大板块(4)——链表
參考:五大板塊(4)——鏈表
作者:丶PURSUING
發布時間: 2021-02-15 09:33:29
網址:https://blog.csdn.net/weixin_44742824/article/details/114981905
目錄
- 一、對比鏈表與數組
- 同樣是存放一串數據,鏈表與數組的區別在哪里?
- 鏈表方便增刪
- 二、鏈表的靜態創建
- 鏈表的動態遍歷:統計節點個數與查找節點
- 三、插入節點與刪除節點
- 從指定節點的后方插入新節點
- 在指定節點前方插入新節點
- 刪除指定節點
- 四、鏈表的動態創建
- 頭插法創建鏈表
- 尾插法創建鏈表
一、對比鏈表與數組
同樣是存放一串數據,鏈表與數組的區別在哪里?
數組是申請連續的地址存放數據,在增加或刪除某一元素不方便。
而鏈表可以很好地解決這個問題。
鏈表方便增刪
大致思路:
- 增加節點
- 刪除節點
二、鏈表的靜態創建
#include <stdio.h>struct Test {int data;struct Test *next; };int main() {struct Test t1 ={1,NULL};struct Test t2 ={2,NULL};struct Test t3 ={3,NULL};t1.next = &t2;//t1的指針指向了t2的地址t2.next = &t3;//t1.next是一個結構體指針,訪問里面的data自然要用->printf("%d %d %d\n",t1.data,t1.next->data,t1.next->next->data);return 0; }鏈表的動態遍歷:統計節點個數與查找節點
#include <stdio.h>struct Test {int data;struct Test *next; };//遍歷鏈表,把節點數據打印出來 void printLink(struct Test *head) {int i;struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;} } //統計鏈表節點個數 void getNodeNum(struct Test *head) {int cnt = 0;struct Test *p = head;while(p != NULL){cnt++;p = p->next;}printf("鏈表節點的個數是:%d\n",cnt); }//找節點 void findNode(struct Test *head,int data) {struct Test *p = head;while(p != NULL){if(p->data == data){printf("找到了\n");return;//直接退出子函數,返回main函數}p = p->next;}printf("沒找到\n"); }int main() {struct Test t1 ={1,NULL};struct Test t2 ={2,NULL};struct Test t3 ={3,NULL};t1.next = &t2;//t1的指針指向了t2的地址t2.next = &t3;printLink(&t1);getNodeNum(&t1);findNode(&t1,2);return 0; }結果:
1 2 3 鏈表節點的個數是:3 找到了- 1
- 2
要重點理解的是:p = p->next
指針p指向了下一個結構體的地址,p->next中存放的正是下一個鏈表節點的地址。
p本身是一個結構體指針,所以用->訪問成員next.
三、插入節點與刪除節點
從指定節點的后方插入新節點
思路:
(1)找到指定節點
(2)把指定節點的的next指向new節點的地址
(3)new節點的next指向下一個節點
靠,真拗口,看圖!
舉例:要從鏈表1 2 3 4 中,在 2 后插入 5 。
結果:
1 2 5 3 4- 1
思考一下,為什么上面要傳入結構體new的地址?
像下圖一樣修改,傳入的是結構體變量new,然后p->next再指向new的地址不就行啦?還不是一樣把地址串了起來。
void addBehind(struct Test *head,int data,struct Test new) {struct Test *p = head;while(p != NULL){if(data == p->data){new.next = p->next;p->next = &new;//形參函數結束就釋放了 p->next指向這個位置會發生斷錯誤return;}p = p->next;} }addBehind(&t1,2,new);結果是:段錯誤
Segmentation fault- 1
為啥?
因為上述中new只是子函數的一個形式參數罷了,地址空間是臨時分配,當函數調用結束空間回收,你讓一個指針p->next指向這里,必然導致段錯誤!
在指定節點前方插入新節點
第一種情況:不是1之前插入,鏈表頭未發生改變
第二種情況:是在1之前插入,鏈表頭發生改變
舉個栗子:(1)要從鏈表1 2 3 4 中,在 3 前插入 5 。
#include <stdio.h>struct Test {int data;struct Test *next; };struct Test *addInfront(struct Test *head,int data,struct Test *new) {struct Test *p = head;if(data == head->data){new->next = head; //先連新節點的后面head = new;return head;}while(p->next != NULL){if(data == p->next->data){new->next = p->next;//先連新節點的后面p->next = new; //再連新節點的前面return head;}p = p->next;//讓鏈表遍歷起來} }void printLink(struct Test *head) {int i;struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n'); }int main() {struct Test t1 ={1,NULL};struct Test t2 ={2,NULL};struct Test t3 ={3,NULL};struct Test t4 ={4,NULL};t1.next = &t2;//t1的指針指向了t2的地址t2.next = &t3;t3.next = &t4;struct Test new ={5,NULL};struct Test *head = &t1;head = addInfront(head,3,&new);printLink(head);return 0; }結果:
1 2 5 3 4- 1
(2)更改程序,在1之前插入5,結果:
5 1 2 3 4- 1
刪除指定節點
當刪除的是頭節點時,還要注意新頭的替換!
舉例:刪除 1 2 3 4中的 1
#include <stdio.h>struct Test {int data;struct Test *next; };struct Test *deNode(struct Test *head,int data) {struct Test *p = head;if(data == head->data){head = head->next;return head;}while(p->next != NULL){if(data == p->next->data){p->next = p->next->next;return head;}p = p->next;} }void printLink(struct Test *head) {int i;struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n'); }int main() {struct Test t1 ={1,NULL};struct Test t2 ={2,NULL};struct Test t3 ={3,NULL};struct Test t4 ={4,NULL};t1.next = &t2;//t1的指針指向了t2的地址t2.next = &t3;t3.next = &t4;struct Test *head = &t1;head = deNode(head,1);printLink(head);return 0; }結果:
2 3 4- 1
刪除 1 2 3 4中的4,結果:
1 2 3- 1
四、鏈表的動態創建
頭插法創建鏈表
頭一直是在變化的
關鍵步驟:
new->next = head;//new直接指向原來的鏈表頭
head = new;//賦予新的鏈表頭
實際例子:
運用頭插法創建鏈表,直接輸入數據自動串成鏈表,想要結束時,輸入數據999.
#include <stdio.h> #include <stdlib.h> #include <string.h>typedef struct test {int data;struct test *next; }test,*ptest;void printLink(ptest head) {int i;ptest p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n'); }ptest insertHead(ptest head,ptest new) {if(head == NULL){head = new;}else{new->next = head;//先連新節點的后面 new往前拱head = new; //再連新節點的前面 new變成新頭(頭插)}return head;}ptest creatLink(ptest head) {ptest new;while(1){new = (ptest)malloc(sizeof(test));printf("請輸入新的節點,輸入999結束輸入\n");scanf("%d",&new->data);if(new->data == 999){free(new);new = NULL;return head;}head = insertHead(head,new);} }int main() {ptest head = NULL;head = creatLink(head);printLink(head);return 0; }結果:
請輸入新的節點,輸入999結束輸入 3 請輸入新的節點,輸入999結束輸入 4 請輸入新的節點,輸入999結束輸入 5 請輸入新的節點,輸入999結束輸入 6 請輸入新的節點,輸入999結束輸入 999 6 5 4 3- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
尾插法創建鏈表
關鍵步驟:
(1)遍歷找到鏈表的尾部
while(p->next != NULL){p = p->next; }- 1
- 2
- 3
(2)在尾部添加new
p->next = new;- 1
實際例子:
運用尾插法創建鏈表,直接輸入數據自動串成鏈表,想要結束時,輸入數據999.
#include <stdio.h> #include <stdlib.h> #include <string.h>typedef struct test {int data;struct test *next; }test,*ptest;void printLink(ptest head) {int i;ptest p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n'); }ptest insertTail(ptest head,ptest new) {ptest p = head;if(p == NULL){head = new;return head;//沒有此句段錯誤}while(p->next != NULL){p = p->next;//遍歷找到尾巴}p->next = new;//new跟在屁股后面(尾插)return head; }ptest creatLink(ptest head) {ptest new;while(1){new = (ptest)malloc(sizeof(test));printf("請輸入新的節點,輸入999結束輸入\n");scanf("%d",&new->data);if(new->data == 999){free(new);new = NULL;return head;}head = insertTail(head,new);} }int main() {ptest head = NULL;head = creatLink(head);printLink(head);return 0; }結果:
請輸入新的節點,輸入999結束輸入 3 請輸入新的節點,輸入999結束輸入 4 請輸入新的節點,輸入999結束輸入 5 請輸入新的節點,輸入999結束輸入 999 3 4 5- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
思考:當上面的inserTail函數更改為如下,會發生什么?
ptest insertTail(ptest head,ptest new) {ptest p = head;if(head == NULL){head = new;}else{p->next = new;}return head; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
結果:可以發現無論怎樣輸入鏈表都只有第一個和最后一個數據
請輸入新的節點,輸入999結束輸入 1 請輸入新的節點,輸入999結束輸入 3 請輸入新的節點,輸入999結束輸入 5 請輸入新的節點,輸入999結束輸入 6 請輸入新的節點,輸入999結束輸入 8 請輸入新的節點,輸入999結束輸入 999 1 8- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
那是因為:使用尾插法,鏈表頭一直未改變。然而在每一次的循環中,p->next都指向new,即為每次頭都指向new。到最后鏈表中自然只有頭和最新的new啦。
總結
以上是生活随笔為你收集整理的五大板块(4)——链表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅谈前端是否需要精通JS三大框架,vue
- 下一篇: Eclips将lib打入war中