網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之雙鏈表&循環(huán)鏈表&靜態(tài)鏈表詳解_C 語(yǔ)言
作者:程序喵正在路上 ? 更新時(shí)間: 2022-11-16 編程語(yǔ)言單鏈表 VS 雙鏈表
我們都知道,單鏈表只有一個(gè)指向下一個(gè)結(jié)點(diǎn)的指針,當(dāng)我們想要找到前一個(gè)結(jié)點(diǎn)時(shí)就比較麻煩,而雙鏈表?yè)碛袃蓚€(gè)指針
總的來(lái)說(shuō):
- 單鏈表 —— 無(wú)法逆向檢索,有時(shí)候不太方便
- 雙鏈表 —— 可進(jìn)可退,存儲(chǔ)密度更低一丟丟
定義雙鏈表結(jié)點(diǎn)類型
typedef struct DNode{
ElemType data; //數(shù)據(jù)域
struct DNode *prior, *next; //前驅(qū)和后繼指針
}DNode, *DLinklist;
雙鏈表
雙鏈表的初始化(帶頭結(jié)點(diǎn))
定義一個(gè) InitLinklist 函數(shù),參數(shù)為雙鏈表的引用,加引用是因?yàn)橐淖冞@個(gè)雙鏈表
注意:頭結(jié)點(diǎn)的前驅(qū)指針永遠(yuǎn)指向 NULL
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct DNode{
ElemType data; //數(shù)據(jù)域
struct DNode *prior, *next; //前驅(qū)和后繼指針
}DNode, *DLinklist;
//初始化雙鏈表
bool InitLinklist(DLinklist &L) {
L = (DNode *)malloc(sizeof(DNode)); //分配一個(gè)頭結(jié)點(diǎn)
if (L == NULL) return false; //內(nèi)存不足,分配失敗
L->prior = NULL; //頭結(jié)點(diǎn)的 prior 永遠(yuǎn)指向 NULL
L->next = NULL; //頭結(jié)點(diǎn)之后暫時(shí)還沒(méi)有結(jié)點(diǎn)
return true;
}
//判斷雙鏈表是否為空(帶頭結(jié)點(diǎn))
bool Empty(DLinklist L) {
if (L->next == NULL)
return true;
else
return false;
}
void testDLinklist() {
//初始化雙鏈表
DLinklist L;
InitLinklist(L);
}
雙鏈表的插入
后插法
//在p結(jié)點(diǎn)之后插入s結(jié)點(diǎn)
bool InsertNextDNode(DNode *p, DNode *s) {
if (p == NULL || s == NULL) return false; //非法參數(shù)
s->next = p->next;
if (p->next != NULL) //如果p結(jié)點(diǎn)有后繼結(jié)點(diǎn)
p->next->prior = s;
s->prior = p;
p->next = s;
return true;
}
學(xué)會(huì)了后插操作,我們也就學(xué)會(huì)了按位序插入和前插法,大概思路為找到目標(biāo)結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn),然后對(duì)其進(jìn)行后插操作
雙鏈表的刪除
//刪除p結(jié)點(diǎn)的后繼結(jié)點(diǎn)
bool DeleteNextDNode(DNode *p) {
if (p == NULL) return false;
DNode *q = p->next; //找到p結(jié)點(diǎn)的后繼結(jié)點(diǎn)q
if (q == NULL) return false; //p沒(méi)有后繼
p->next = q->next;
if (q->next != NULL) //q結(jié)點(diǎn)不是最后一個(gè)結(jié)點(diǎn)
q->next->prior = p;
free(p); //釋放結(jié)點(diǎn)空間
return true;
}
//銷毀雙鏈表
void DestoryList(DLinklist &L) {
//循環(huán)釋放各個(gè)數(shù)據(jù)結(jié)點(diǎn)
while (L->next != NULL) {
DeleteNextDNode(L);
}
free(L); //釋放頭結(jié)點(diǎn)
L = NULL; //頭指針指向NULL
}
雙鏈表的遍歷
由于雙鏈表不可隨機(jī)存取,所以按位查找、按值查找等操作都只能用遍歷的方式實(shí)現(xiàn),時(shí)間復(fù)雜度為 O(n)
//后向遍歷
while (p != NULL) {
//對(duì)結(jié)點(diǎn)p做相應(yīng)處理,比如打印
p = p->next;
}
//前向遍歷
while (p != NULL) {
//對(duì)結(jié)點(diǎn)p做相應(yīng)處理
p = p->prior;
}
//前向遍歷(跳過(guò)頭結(jié)點(diǎn))
while (p->prior != NULL) {
//對(duì)結(jié)點(diǎn)p做相應(yīng)處理
p = p->prior;
}
循環(huán)單鏈表
我們都知道,單鏈表的表尾結(jié)點(diǎn)的 next 指針是指向 NULL,顧名思義,循環(huán)單鏈表的表尾結(jié)點(diǎn)的 next 指針就是指向頭結(jié)點(diǎn)的
循環(huán)單鏈表的優(yōu)點(diǎn):從一個(gè)結(jié)點(diǎn)出發(fā)可以找到其他任何一個(gè)結(jié)點(diǎn)
typedef int ElemType;
typedef struct LNode{
ElemType data; //每個(gè)節(jié)點(diǎn)存放一個(gè)數(shù)據(jù)元素
struct LNode *next; //指針指向下一個(gè)節(jié)點(diǎn)
}LNode, *LinkList;
//初始化一個(gè)循環(huán)單鏈表
bool InitList(LinkList &L) {
L = (LNode *)malloc(sizeof(LNode)); //分配一個(gè)頭結(jié)點(diǎn)
if (L == NULL) return false; //內(nèi)存不足,分配失敗
L->next = L; //頭結(jié)點(diǎn)next指針指向頭結(jié)點(diǎn)
return true;
}
//判斷循環(huán)單鏈表是否為空
bool Empty(LinkList L) {
if (L->next == L)
return true;
else
return false;
}
//判斷結(jié)點(diǎn)p是否為循環(huán)單鏈表的表尾結(jié)點(diǎn)
bool isTail(LinkList L, LNode *p) {
if (p->next == L)
return true;
else
return false;
}
循環(huán)雙鏈表
雙鏈表:
- 表頭結(jié)點(diǎn)的 prior 指向 NULL
- 表尾結(jié)點(diǎn)的 next 指向 NULL
循環(huán)雙鏈表
- 表頭結(jié)點(diǎn)的 prior 指向表尾結(jié)點(diǎn)
- 表尾結(jié)點(diǎn)的 next 指向頭結(jié)點(diǎn)
循環(huán)雙鏈表的初始化
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct DNode{
ElemType data; //數(shù)據(jù)域
struct DNode *prior, *next; //前驅(qū)和后繼指針
}DNode, *DLinklist;
//初始化空的循環(huán)雙鏈表
bool InitDLinklist(DLinklist &L) {
L = (DNode *)malloc(sizeof(DNode)); //分配一個(gè)頭結(jié)點(diǎn)
if (L == NULL) return false; //內(nèi)存不足,分配失敗
L->prior = L; //頭結(jié)點(diǎn)的 prior 指向頭結(jié)點(diǎn)
L->next = L; //頭結(jié)點(diǎn)的 next 指向頭結(jié)點(diǎn)
return true;
}
//判斷循環(huán)雙鏈表是否為空
bool Empty(DLinklist L) {
if (L->next == L)
return true;
else
return false;
}
//判斷結(jié)點(diǎn)p是否為循環(huán)雙鏈表的表尾結(jié)點(diǎn)
bool isTail(DLinklist L, DNode *p) {
if (p->next = L)
return true;
else
return false;
}
void testDLinklist() {
//初始化雙鏈表
DLinklist L;
InitDLinklist(L);
}
循環(huán)雙鏈表的插入
//在p結(jié)點(diǎn)之后插入s節(jié)點(diǎn)
bool InsertNextDNode(DNode *p, DNode *s) {
s->next = p->next;
p->next->prior = s;
s->prior = p;
p->next = s;
return true;
}
循環(huán)雙鏈表的刪除
//刪除p的后繼結(jié)點(diǎn)q
p->next = q->next;
q->next->prior = p;
free(q);
靜態(tài)鏈表
什么是靜態(tài)鏈表
單鏈表:各個(gè)結(jié)點(diǎn)在內(nèi)存中星羅棋布、散落天涯
靜態(tài)鏈表:分配一整片連續(xù)的內(nèi)存空間,各個(gè)結(jié)點(diǎn)集中安置,0 號(hào)結(jié)點(diǎn)充當(dāng) “頭結(jié)點(diǎn)”,下一個(gè)結(jié)點(diǎn)的數(shù)組下標(biāo)(也稱為游標(biāo))充當(dāng) “指針”,游標(biāo)為 -1 時(shí)表示已經(jīng)到達(dá)表尾
靜態(tài)鏈表是用數(shù)組的方式來(lái)實(shí)現(xiàn)的鏈表,其優(yōu)點(diǎn)為 —— 增、刪操作不需要大量移動(dòng)元素;缺點(diǎn)為 —— 不能隨機(jī)存取,只能從頭結(jié)點(diǎn)開(kāi)始依次往后查找;容量固定不可變
定義靜態(tài)鏈表
#define MaxSize 10 //靜態(tài)鏈表的最大長(zhǎng)度
struct Node{
ElemType data; //存儲(chǔ)數(shù)據(jù)元素
int next; //下一個(gè)元素的數(shù)組下標(biāo)
};
或者
#define MaxSize 10 //靜態(tài)鏈表的最大長(zhǎng)度
typedef struct {
ElemType data; //存儲(chǔ)數(shù)據(jù)元素
int next; //下一個(gè)元素的數(shù)組下標(biāo)
} SLinkList[MaxSize];
SLinkList a 相當(dāng)于 struct Node a[MaxSize]
基本操作的實(shí)現(xiàn)
初始化
把 a[0] 的 next 設(shè)置為 -1
把空的結(jié)點(diǎn)的 next 設(shè)置為 -2
查找
從頭結(jié)點(diǎn)出發(fā)依次往后遍歷結(jié)點(diǎn)
插入位序?yàn)?i 的結(jié)點(diǎn)
- 找到一個(gè)空的結(jié)點(diǎn),存入數(shù)據(jù)元素
- 從頭結(jié)點(diǎn)出發(fā)找到位序?yàn)?i-1 的結(jié)點(diǎn)
- 修改新結(jié)點(diǎn)的 next
- 修改 i-1 號(hào)結(jié)點(diǎn)的 next
刪除某個(gè)結(jié)點(diǎn)
- 從頭結(jié)點(diǎn)出發(fā)找到前驅(qū)結(jié)點(diǎn)
- 修改前驅(qū)結(jié)點(diǎn)的游標(biāo)
- 被刪除結(jié)點(diǎn)的 next 設(shè)置為 -2
順序表和鏈表的比較
從邏輯結(jié)構(gòu)來(lái)說(shuō),順序表和鏈表都屬于線性表,都是線性結(jié)構(gòu)
從存儲(chǔ)結(jié)構(gòu)來(lái)說(shuō),順序表采用順序存儲(chǔ),而鏈表采用鏈?zhǔn)酱鎯?chǔ)
順序表
優(yōu)點(diǎn):支持隨機(jī)存取,存取密度高
缺點(diǎn):大片連續(xù)空間分配不方便,改變?nèi)萘坎环奖?/p>
鏈表:
優(yōu)點(diǎn):離散的小空間分配方便,改變?nèi)萘糠奖?/p>
缺點(diǎn):不可隨機(jī)存取,存儲(chǔ)密度低
從基本操作來(lái)看
創(chuàng)
- 順序表需要預(yù)分配大片連續(xù)空間,若分配空間過(guò)小,則之后不方便擴(kuò)展容量;若分配空間過(guò)大,則浪費(fèi)內(nèi)存資源。如果采取靜態(tài)分配的方式,則容量不可改變;如果采取動(dòng)態(tài)分配的方式,則容量可改變,但需要移動(dòng)大量元素,時(shí)間代價(jià)高
- 鏈表只需分配一個(gè)頭結(jié)點(diǎn)(也可以不要頭結(jié)點(diǎn),只聲明一個(gè)頭指針),之后方便拓展
銷
- 對(duì)鏈表來(lái)說(shuō),你只需掃描整個(gè)鏈表,依次刪除(free)各個(gè)結(jié)點(diǎn)即可
- 對(duì)順序表來(lái)說(shuō),首先你需要修改 length = 0,如果是采用靜態(tài)分配的方式,當(dāng)靜態(tài)數(shù)組的生命周期結(jié)束時(shí),系統(tǒng)會(huì)自動(dòng)回收空間;如果是采用動(dòng)態(tài)分配的方式,用 malloc 申請(qǐng)的空間是屬于內(nèi)存中的堆區(qū),在堆區(qū)的內(nèi)存空間不會(huì)由系統(tǒng)自動(dòng)回收,需要我們手動(dòng) free
增刪
- 對(duì)順序表來(lái)說(shuō),插入或刪除都要講后續(xù)元素全部后移或前移,時(shí)間復(fù)雜度為 O(n),時(shí)間開(kāi)銷主要來(lái)自移動(dòng)元素
- 對(duì)鏈表來(lái)說(shuō),插入或刪除元素只需要修改指針即可,時(shí)間復(fù)雜度為 O(n),時(shí)間開(kāi)銷主要來(lái)自查找目標(biāo)元素
- 雖然時(shí)間復(fù)雜度一樣,但是結(jié)合實(shí)際因素,鏈表增刪的效率要比順序表高得多
查
- 對(duì)順序表來(lái)說(shuō),按位查找的時(shí)間復(fù)雜度為 O(1);按值查找的時(shí)間復(fù)雜度為 O(n),如果表內(nèi)元素有序,可采用折半查找等方法在 O(log2n) 時(shí)間內(nèi)找到
- 對(duì)鏈表來(lái)說(shuō),按位查找的時(shí)間復(fù)雜度為 O(n);按值查找的時(shí)間復(fù)雜度也為 O(n)
綜上所述
- 表長(zhǎng)難以預(yù)估、經(jīng)常要增加或刪除元素 —— 鏈表
- 表長(zhǎng)可預(yù)估、查詢操作較多 —— 順序表
原文鏈接:https://blog.csdn.net/weixin_62511863/article/details/127020407
相關(guān)推薦
- 2022-02-02 es 同步索引報(bào)錯(cuò):ElasticSearch ClusterBlockException[bloc
- 2022-06-17 GO語(yǔ)言協(xié)程互斥鎖Mutex和讀寫鎖RWMutex用法實(shí)例詳解_Golang
- 2022-11-25 React之echarts-for-react源碼解讀_React
- 2022-10-29 C#?CLR?中學(xué)習(xí)?C++關(guān)鍵詞extern使用詳解_C 語(yǔ)言
- 2022-05-03 C#設(shè)計(jì)模式之工廠模式_C#教程
- 2022-07-04 PyCharm如何配置SSH和SFTP連接遠(yuǎn)程服務(wù)器_python
- 2023-02-09 c++報(bào)錯(cuò)問(wèn)題解決方案lvalue?required?as?left?operand?of?assi
- 2022-03-23 shell中的流編輯器awk工作原理_linux shell
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支