本文是小編鞏固自身而作,如有錯誤,歡迎指出!
1.鏈表的概念
概念:鏈表是?種物理存儲結構上?連續、?順序的存儲結構,數據元素的邏輯順序是通過鏈表中的 指針鏈接次序實現的。
和之前的順序表不同,順序一般通過數組實現,每個儲存的元素都是相鄰的,而鏈表不一樣,鏈表的每一個節點并不相連,而是通過鏈表內的指針進行相連,每一個節點可以分為兩個部分,儲存的數據和指向下一個節點的地址。
2.鏈表的性質
1、鏈式機構在邏輯上是連續的,在物理結構上不?定連續
?2、結點?般是從堆上申請的
?3、從堆上申請來的空間,是按照?定策略分配出來的,每次申請的空間可能連續,可能不連續
?
我們在此用結構體創建鏈表的原型
//定義鏈表結構
typedef int sldatatype;
struct slistnode
{sldatatype data;//儲存的數據struct slistnode* next;//指向下一個節點
};
typedef struct slistnode SLTNODE;
?然后我們創建一個簡單的鏈表
void test01()
{SLTNODE* node1 = (SLTNODE*)malloc(sizeof(SLTNODE));SLTNODE* node2 = (SLTNODE*)malloc(sizeof(SLTNODE));SLTNODE* node3 = (SLTNODE*)malloc(sizeof(SLTNODE));SLTNODE* node4 = (SLTNODE*)malloc(sizeof(SLTNODE));node1->data = 1;node2->data = 2;node3->data = 3;node4->data = 4;node1->next = node2;node2->next = node3;node3->next = node4;node4->next = NULL;
}
3.鏈表的打印?
我們現在看看鏈表的打印是怎么實現的
先前我們學習順序表時我們打印時可以直接將指向元素的地址向后挪,而鏈表每一個節點之間是相互獨立的,因此我們只能過通過指針之間的鏈接進行打印
void sltprint(SLTNODE* phead)
{SLTNODE* pcur = phead;while (pcur){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("NULL\n");
}
在這個代碼中,我們將每一個節點通過指針的方式進行鏈式訪問。
4.鏈表的頭插尾插
4.1鏈表的節點創建
要想要在鏈表中插入數值,就不能跟循序表一樣進行數據的移動完成,而要通過創立新的節點
SLTNODE* sltbuynode(sldatatype x)
{//創建新的節點SLTNODE* newnode = (SLTNODE*)malloc(sizeof(SLTNODE));if (newnode == NULL){perror("malloc fail");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}
4.2鏈表的尾插
其中的思路就是在鏈表尾部加入一個新的節點
oid sltpushback(SLTNODE** pphead, sldatatype x)
{SLTNODE* newnode= sltbuynode(x);if (*pphead == NULL)//鏈表為空{*pphead = newnode;}else{SLTNODE* ptail = *pphead;while (ptail->next){ptail = ptail->next;}ptail->next = newnode;}}
?其中一個要注意的點當鏈表為空時,就直接將其作為頭節點即可,還有一個值得思考的問題即為什么這里使用了二級指針?
其原因在于如果傳過去的是節點,其中形參的改變并不會影響實參,即整個函數運行結束后,仍不會改變原來的節點
4.3鏈表的頭插?
void sltpushfrond(SLTNODE** pphead, sldatatype x)
{assert(*pphead);SLTNODE* newnode = sltbuynode(x);newnode->next = *pphead;*pphead = newnode;//讓指向第一個節點的位置變化
}
不如尾插還需要遍歷,頭插只需要將 創建的新節點指向的位置改為之前的頭結點即可
5.鏈表的頭刪與尾刪
5.1尾刪
void sltpopback(SLTNODE** pphead)
{assert(pphead && *pphead);//只有一個節點if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else {SLTNODE* prev = NULL;SLTNODE* ptail = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;}
}
?思路與尾插相同,把尾部的最后一個節點釋放,并將其前一個節點的指向位置改為空指針即可
就是要考慮到一開始就只有一個節點的情況
5.2頭刪
void sltpopfront(SLTNODE** pphead)
{assert(pphead && *pphead);SLTNODE* next = (*pphead)->next;free(*pphead);*pphead=next;
}
直接將第一個節點釋放?,并將頭節點的地址改為第二個節點的地址
6.單鏈表的查找
SLTNODE* sltfind(SLTNODE* phead, sldatatype x)
{SLTNODE* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}
?其主要思路就是將整個鏈表遍歷找出值相同的節點,找到了就返回節點地址,沒找到就返回NULL
7.鏈表的在任意節點的插值
7.1在位置前插值
void sltinsert(SLTNODE** pphead, SLTNODE* pos, sldatatype x)
{assert(pphead && pos);//指向第一個節點時if (pos == *pphead){sltpushfrond(*pphead,x);}SLTNODE* newnode = sltbuynode(x);SLTNODE* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;
}
先遍歷鏈表找到插值位置的前一個節點,?并將其指向的節點改變成插入的節點,并將插入的節點指向的位置設置為下一個節點
7.2在位置后插值
void sltinsertafter(SLTNODE* pos, sldatatype x)
{assert(pos);SLTNODE* newnode = sltbuynode(x);newnode->next = pos->next;pos->next = newnode;
}
直接改變兩個指向地址即可
8.鏈表的刪除
8.1指定位置的刪除
void slterase(SLTNODE** pphead, SLTNODE* pos)
{assert(pphead && pos);//pos頭結點if (pos == *pphead){sltpopfront(*pphead);}else {SLTNODE* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}
?其思路與插入相似,即將上一個節點的指向位置指向下一個節點即可,然后將原本的節點釋放
8.2指定位置后一個數的刪除
void slteraseafter(SLTNODE* pos)
{assert(pos&&pos->next);SLTNODE* del = pos->next;pos->next = del->next;free(del);del = NULL;
}
不需要遍歷,直接改變本身的指向位置即可?
9.鏈表的銷毀
void slistdestory(SLTNODE** pphead)
{SLTNODE* pcur = *pphead;while (pcur){SLTNODE* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}
利用循環將每一個節點依次釋放,并將首節點置空即可。
10.完整代碼實現
頭文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//定義鏈表結構
typedef int sldatatype;
struct slistnode
{sldatatype data;//儲存的數據struct slistnode* next;//指向下一個節點
};
typedef struct slistnode SLTNODE;
void sltprint(SLTNODE* phead);//打印鏈表
void sltpushback(SLTNODE** pphead, sldatatype x);//尾插
SLTNODE* sltbuynode(sldatatype x);//創建新的節點
void sltpushfrond(SLTNODE** pphead, sldatatype x);//頭插
void sltpopback(SLTNODE** pphead);//尾刪
void sltpopfront(SLTNODE** pphead);//頭刪
SLTNODE* sltfind(SLTNODE* phead, sldatatype x);//查找
void sltinsert(SLTNODE** pphead, SLTNODE* pos, sldatatype x);//在指定數據前插入數據
void sltinsertafter( SLTNODE* pos, sldatatype x);//在指定數據后插入數據
void slterase(SLTNODE** pphead, SLTNODE* pos);//刪除pos節點
void slteraseafter(SLTNODE* pos);//刪除pos的下一個節點
void slistdestory(SLTNODE** pphead);//銷毀鏈表
源文件
#define _CRT_SECURE_NO_WARNINGS
#include"single_list.h"void sltprint(SLTNODE* phead)
{SLTNODE* pcur = phead;while (pcur){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("NULL\n");
}
SLTNODE* sltbuynode(sldatatype x)
{//創建新的節點SLTNODE* newnode = (SLTNODE*)malloc(sizeof(SLTNODE));if (newnode == NULL){perror("malloc fail");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}
void sltpushback(SLTNODE** pphead, sldatatype x)
{SLTNODE* newnode= sltbuynode(x);if (*pphead == NULL)//鏈表為空{*pphead = newnode;}else{SLTNODE* ptail = *pphead;while (ptail->next){ptail = ptail->next;}ptail->next = newnode;}}
void sltpushfrond(SLTNODE** pphead, sldatatype x)
{assert(*pphead);SLTNODE* newnode = sltbuynode(x);newnode->next = *pphead;*pphead = newnode;//讓指向第一個節點的位置變化
}
void sltpopback(SLTNODE** pphead)
{assert(pphead && *pphead);//只有一個節點if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else {SLTNODE* prev = NULL;SLTNODE* ptail = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;}
}
void sltpopfront(SLTNODE** pphead)
{assert(pphead && *pphead);SLTNODE* next = (*pphead)->next;free(*pphead);*pphead=next;
}
SLTNODE* sltfind(SLTNODE* phead, sldatatype x)
{SLTNODE* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}
void sltinsert(SLTNODE** pphead, SLTNODE* pos, sldatatype x)
{assert(pphead && pos);//指向第一個節點時if (pos == *pphead){sltpushfrond(*pphead,x);}SLTNODE* newnode = sltbuynode(x);SLTNODE* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;
}
void sltinsertafter(SLTNODE* pos, sldatatype x)
{assert(pos);SLTNODE* newnode = sltbuynode(x);newnode->next = pos->next;pos->next = newnode;
}
void slterase(SLTNODE** pphead, SLTNODE* pos)
{assert(pphead && pos);//pos頭結點if (pos == *pphead){sltpopfront(*pphead);}else {SLTNODE* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}
void slteraseafter(SLTNODE* pos)
{assert(pos&&pos->next);SLTNODE* del = pos->next;pos->next = del->next;free(del);del = NULL;
}
void slistdestory(SLTNODE** pphead)
{SLTNODE* pcur = *pphead;while (pcur){SLTNODE* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}
測試文件
#define _CRT_SECURE_NO_WARNINGS
#include"single_list.h"
void test01()
{SLTNODE* node1 = (SLTNODE*)malloc(sizeof(SLTNODE));SLTNODE* node2 = (SLTNODE*)malloc(sizeof(SLTNODE));SLTNODE* node3 = (SLTNODE*)malloc(sizeof(SLTNODE));SLTNODE* node4 = (SLTNODE*)malloc(sizeof(SLTNODE));node1->data = 1;node2->data = 2;node3->data = 3;node4->data = 4;node1->next = node2;node2->next = node3;node3->next = node4;node4->next = NULL;SLTNODE* plist = node1;sltprint(plist);}
void test02()
{SLTNODE* plist = NULL;//創建空鏈表sltpushback(&plist, 1);sltpushback(&plist, 1);sltpushback(&plist, 2);sltpushback(&plist, 4);sltpushback(&plist, 3);sltpushfrond(&plist, 5);sltpopfront(&plist);sltpopback(&plist);sltprint(plist);SLTNODE* find= sltfind(plist, 4);/*if (find){printf("找到了\n");}else{printf("未找到\n");}*//*sltinsert(&plist, find, 100);*/sltinsertafter(find, 50);//slterase(&plist, find);/*slteraseafter(find);*/slistdestory(&plist);sltprint(plist);
}
int main()
{test01();/*test02();*/return 0;
}
本次分享就到這里,后續會繼續更新,感謝閱讀!