網站首頁 編程語言 正文
在上一篇所講述的 動態順序表 中存在一些缺陷
1、當空間不夠時需要擴容,擴容是有一定的消耗的
如果每次空間擴大一點,可能會造成空間的浪費,而空間擴小了,又會造成頻繁的擴容2、在順序表中進行頭部和中部的插入時需要移動數據,效率低下
對于順序表的這些缺陷,有如下解決方案
1、需要時申請一塊空間,不需要時將其釋放
2、插入刪除不需要移動數據
而鏈表就符合這兩點,本篇介紹 無頭單向非循環鏈表(單鏈表)
一、單鏈表的結構
空鏈表: 此時沒有存儲數據,只有一個指針指向 NULL
以上便是單鏈表的結構:
- 每一塊空間可以按需申請釋放
- 插入和刪除不需要移動數據,修改每塊空間的指針指向即可
在習慣上將申請的一塊一塊的空間稱為結點,指向第一個結點的指針稱為頭指針
//數據的類型:這里以 int 來舉例
typedef int SLTDataType;
//結點的類型
typedef struct SListNode
{
SLTDataType data;
struct SListNode* next;
}SLTNode;
二、單鏈表的函數接口
1. 申請結點及打印單鏈表
在插入時需要申請結點,為了避免麻煩重復的操作,這里將申請結點封裝為一個函數
申請結點函數如下:
SLTNode* BuySLTNode(SLTDataType x)
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if(newnode == NULL)
{
//開辟空間失敗,打印錯誤信息
perror("malloc");
//結束程序
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
為了驗證插入、刪除等得到的結果是否正確,提供打印單鏈表的函數,這里數據類型以 int 為例,當讀者采用的類型不同時,自行更改函數即可
打印單鏈表函數如下:
void SLTPrint(SLTNode* phead)
{
SLTNode* cur = phead;
//打印數據
while(cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
2. 尾插尾刪
尾插:在鏈表的最后一個結點之后插入結點
尾插函數如下:
//在鏈表為空時,需要改變頭指針,這里采用傳二級指針的方式
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
//申請結點
SLTNode* newnode = BuySLTNode(x);
//鏈表為空時
if(*pphead == NULL)
{
*pphead = newnode;
}
else
{
//找到最后一個結點
SLTNode* ptail = *pphead;
while(ptail->next)
{
ptail = ptail->next;
}
ptail->next = newnode;
}
}
尾刪:刪除鏈表最后一個結點
尾刪函數如下:
//鏈表只有一個結點時,需要改變頭指針,這里采用傳二級指針的方式
void SLTPopBack(SLTNode** pphead)
{
//鏈表為空時,無法刪除
assert(*pphead);
//鏈表只有一個結點時
if((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
//找到倒數第二個結點
SLTNode* ptail = *pphead;
while(ptail->next->next)
{
ptail = ptail->next;
}
free(ptail->next);
ptail->next = NULL;
}
}
3. 頭插頭刪
頭插: 在第一個結點之前插入新結點
頭插函數如下:
//需要改變頭指針,這里采用傳二級指針的方式
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
//申請結點
SLTNode* newnode = BuySLTNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
頭刪:刪除鏈表的第一個結點
頭刪函數如下:
//需要改變頭指針,這里采用傳二級指針的方式
void SLTPopFront(SLTNode** pphead)
{
//鏈表為空時,無法刪除
assert(*pphead);
//保存第二個結點
SLTNode* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
4. 中間插入和刪除
中間插入:通過后面介紹的查找函數 SLTFind 獲得指向結點的指針 pos,在 pos 指向的 結點之前 或 之后 插入結點
1. 在 pos 指向的結點之后插入結點
在 pos 之后插入結點函數如下:
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
//pos 不能為空
assert(pos);
//申請結點
SLTNode* newnode = BuySLTNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
2. 在 pos 指向的結點之前插入結點
在 pos 之前插入結點函數如下:
//pos 指向頭結點時,需要改變頭指針,這里采用傳二級指針的方式
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
//pos 不能為空
assert(pos);
//頭插
if(*pphead == pos)
{
SLTPushFront(pphead, x);
}
else
{
//找到 pos 的前一個結點
SLTNode* prev = *pphead;
while(prev->next != pos)
{
prev = prev->next;
}
//申請結點
SLTNode* newnode = BuySLTNode(x);
newnode->next = pos;
prev->next = newnode;
}
}
中間刪除:通過后面介紹的查找函數 SLTFind 獲得指向結點的指針 pos,刪除 pos 指向的結點 或 后一個結點
3. 刪除 pos 指向的結點的后一個結點
刪除 pos 之后的結點函數如下:
void SLTEraseAfter(SLTNode* pos)
{
//pos 不能為空
assert(pos);
//指向最后一個結點時,不做處理
if(pos->next == NULL)
{
return;
}
else
{
//保存后一個結點
SLTNode* next = pos->next;
pos->next = next->next;
free(next);
}
}
4. 刪除 pos 指向的結點
刪除 pos 指向的結點函數如下:
//pos 指向頭結點時,需要改變頭指針,這里采用傳二級指針的方式
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
//pos 不能為空
assert(pos);
//頭刪
if (*pphead == pos)
{
SLTPopFront(pphead);
}
else
{
//找到 pos 的前一個結點
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
6. 查找
查找:如果數據存在,返回該數據結點的指針,不存在返回 NULL
查找函數如下:
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
SLTNode* cur = phead;
//查找
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
7. 銷毀單鏈表
在單鏈表中,存儲數據的結點是由自己開辟的,當不使用單鏈表時,應將其銷毀
銷毀單鏈表函數如下:
需要將頭指針置空,這里采用傳二級指針的方式
void SLTDestroy(SLTNode** pphead)
{
SLTNode* cur = *pphead;
while (cur)
{
//保存下一個結點
SLTNode* nextnode = cur->next;
free(cur);
cur = nextnode;
}
//將頭指針置空
*pphead = NULL;
}
原文鏈接:https://blog.csdn.net/qq_70793373/article/details/127782089
相關推薦
- 2023-01-23 使用Docker部署Dashdot服務器儀表盤的步驟_docker
- 2022-06-29 docker容器狀態轉換管理命令實例詳解_docker
- 2022-05-08 PyTorch實現多維度特征輸入邏輯回歸_python
- 2022-05-10 C++構造函數+復制構造函數+重載等號運算符調用_C 語言
- 2022-06-02 React中的Props類型校驗和默認值詳解_React
- 2022-04-01 K8s 創建Pod 的時候出現 Error: Error response from daemon:
- 2022-07-27 Go?error的使用方式詳解_Golang
- 2022-08-03 Python?權限控制模塊?Casbin_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支