C語(yǔ)言 超詳細(xì)介紹與實(shí)現(xiàn)線(xiàn)性表中的帶頭雙向循環(huán)鏈表
一、本章重點(diǎn)
- 帶頭雙向循環(huán)鏈表介紹
- 帶頭雙向循環(huán)鏈表常用接口實(shí)現(xiàn)
- 實(shí)現(xiàn)接口總結(jié)
- 在線(xiàn)oj訓(xùn)練與詳解
二、帶頭雙向循環(huán)鏈表介紹
2.1什么是帶頭雙向循環(huán)鏈表?
- 帶頭:存在一個(gè)哨兵位的頭節(jié)點(diǎn),該節(jié)點(diǎn)是個(gè)無(wú)效節(jié)點(diǎn),不存儲(chǔ)任何有效信息,但使用它可以方便我們頭尾插和頭尾刪時(shí)不用判斷頭節(jié)點(diǎn)指向NULL的情況,同時(shí)也不需要改變頭指針的指向,也就不需要傳二級(jí)指針了。?
- 雙向:每個(gè)結(jié)構(gòu)體有兩個(gè)指針,分別指向前一個(gè)結(jié)構(gòu)體和后一個(gè)結(jié)構(gòu)體。
- 循環(huán):最后一個(gè)結(jié)構(gòu)體的指針不再指向NULL,而是指向第一個(gè)結(jié)構(gòu)體。(單向)
- 第一個(gè)結(jié)構(gòu)體的前指針指向最后一個(gè)結(jié)構(gòu)體,最后一個(gè)結(jié)構(gòu)體的后指針指向第一個(gè)結(jié)構(gòu)體(雙向)。
圖解?
2.2最常用的兩種鏈表結(jié)構(gòu)
- 更具有無(wú)頭,單雙向,是否循環(huán)組合起來(lái)有8種結(jié)構(gòu),但最長(zhǎng)用的還是無(wú)頭單向非循環(huán)鏈表和帶頭雙向循環(huán)鏈表
- 無(wú)頭單向非循環(huán)鏈表:結(jié)構(gòu)簡(jiǎn)單,一般不會(huì)單獨(dú)用來(lái)存數(shù)據(jù)。實(shí)際中更多是作為其他數(shù)據(jù)結(jié)構(gòu)的子結(jié)構(gòu),如哈希桶、圖的鄰接表等等。另外這種結(jié)構(gòu)在筆試面試中出現(xiàn)很多。?
- 帶頭雙向循環(huán)鏈表:結(jié)構(gòu)最復(fù)雜,一般用在單獨(dú)存儲(chǔ)數(shù)據(jù)。實(shí)際中使用的鏈表數(shù)據(jù)結(jié)構(gòu),都是帶頭雙向循環(huán)鏈表。另外這個(gè)結(jié)構(gòu)雖然結(jié)構(gòu)復(fù)雜,但是使用代碼實(shí)現(xiàn)以后會(huì)發(fā)現(xiàn)結(jié)構(gòu)會(huì)帶來(lái)很多優(yōu)勢(shì),實(shí)現(xiàn)反而簡(jiǎn)單了,后面我們代碼實(shí)現(xiàn)了就知道了。
三、帶頭雙向循環(huán)鏈表常用接口實(shí)現(xiàn)?
3.1結(jié)構(gòu)體創(chuàng)建
typedef int DataType; typedef struct DListNode { DataType data; DListNode* prev; DListNode* next; }DListNode;
3.2帶頭雙向循環(huán)鏈表的初始化?
void DListInint(DListNode** pphead) { *pphead = (DListNode*)malloc(sizeof(DListNode)); (*pphead)->next = (*pphead); (*pphead)->prev = (*pphead); }
?或者使用返回節(jié)點(diǎn)的方法也能實(shí)現(xiàn)初始化
DListNode* DListInit() { DListNode* phead = (DListNode*)malloc(sizeof(DListNode)); phead->next = phead; phead->prev = phead; return phead; }
3.3創(chuàng)建新節(jié)點(diǎn)
DListNode* BuyDListNode(DataType x) { DListNode* temp = (DListNode*)malloc(sizeof(DListNode)); if (temp == NULL) { printf("malloc fail\n"); exit(-1); } temp->prev = NULL; temp->next = NULL; temp->data = x; return temp; }
3.4尾插
void DListPushBack(DListNode* phead,DataType x) { DListNode* newnode = BuyDListNode(x); DListNode* tail = phead->prev; tail->next = newnode; newnode->prev = tail; newnode->next = phead; phead->prev = newnode; }
3.5打印鏈表
void DListNodePrint(DListNode* phead) { DListNode* cur = phead->next; while (cur != phead) { printf("%d->", cur->data); cur = cur->next; } printf("NULL\n"); }
3.6頭插
void DListNodePushFront(DListNode* phead, DataType x) { DListNode* next = phead->next; DListNode* newnode = BuyDListNode(x); next->prev = newnode; newnode->next = next; newnode->prev = phead; phead->next = newnode; }
3.7尾刪
void DListNodePopBack(DListNode* phead) { if (phead->next == phead) { return; } DListNode* tail = phead->prev; DListNode* prev = tail->prev; prev->next = phead; phead->prev = prev; free(tail); tail = NULL; }
3.8頭刪
void DListNodePopFront(DListNode* phead) { if (phead->next == phead) { return; } DListNode* firstnode = phead->next; DListNode* secondnode = firstnode->next; secondnode->prev = phead; phead->next = secondnode; free(firstnode); firstnode = NULL; }
3.9查找data(返回data的節(jié)點(diǎn)地址)
DListNode* DListNodeFind(DListNode* phead, DataType x) { DListNode* firstnode = phead->next; while (firstnode != phead) { if (firstnode->data == x) { return firstnode; } firstnode = firstnode->next; } return NULL; }
3.10在pos位置之前插入節(jié)點(diǎn)
void DListNodeInsert(DListNode* pos, DataType x) { DListNode* prev = pos->prev; DListNode* newnode = BuyDListNode(x); newnode->next = pos; newnode->prev = prev; prev->next = newnode; pos->prev = newnode; }
3.11刪除pos位置的節(jié)點(diǎn)
void DListNodeErase(DListNode* pos) { DListNode* prev = pos->prev; DListNode* next = pos->next; prev->next = next; next->prev = prev; free(pos); pos = NULL; }
四、實(shí)現(xiàn)接口總結(jié)
- 多畫(huà)圖:能給清晰展示變化的過(guò)程,有利于實(shí)現(xiàn)編程。
- 小知識(shí):head->next既可表示前一個(gè)結(jié)構(gòu)體的成員變量,有可表示后一個(gè)結(jié)構(gòu)體的地址。當(dāng)head->next作為左值時(shí)代表的是成員變量,作右值時(shí)代表的是后一個(gè)結(jié)構(gòu)體的地址。對(duì)于鏈表來(lái)說(shuō)理解這一點(diǎn)非常重要。
- 實(shí)踐:實(shí)踐出真知
- 帶頭雙向循環(huán)鏈表:相比于單鏈表,它實(shí)現(xiàn)起來(lái)更簡(jiǎn)單,不用向單鏈表一樣分情況討論鏈表的長(zhǎng)度。雖然結(jié)構(gòu)較復(fù)雜,但使用起來(lái)更簡(jiǎn)單,更方便。 ?
五、在線(xiàn)oj訓(xùn)練與詳解
鏈表的中間節(jié)點(diǎn)(力扣)
給定一個(gè)頭結(jié)點(diǎn)為?head
?的非空單鏈表,返回鏈表的中間結(jié)點(diǎn)。
如果有兩個(gè)中間結(jié)點(diǎn),則返回第二個(gè)中間結(jié)點(diǎn)。
輸入:[1,2,3,4,5]
輸出:此列表中的結(jié)點(diǎn) 3 (序列化形式:[3,4,5])
返回的結(jié)點(diǎn)值為 3 。 (測(cè)評(píng)系統(tǒng)對(duì)該結(jié)點(diǎn)序列化表述是 [3,4,5])。
注意,我們返回了一個(gè) ListNode 類(lèi)型的對(duì)象 ans,
這樣:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
來(lái)源:力扣(LeetCode)
?思路:快慢指針
取兩個(gè)指針,初始時(shí)均指向head,一個(gè)為快指針(fast)一次走兩步,另一個(gè)為慢指針(slow)一次走一步,當(dāng)快指針滿(mǎn)足fast==NULL(偶數(shù)個(gè)節(jié)點(diǎn))或者fast->next==NULL(奇數(shù)個(gè)節(jié)點(diǎn))時(shí),slow指向中間節(jié)點(diǎn),返回slow即可。
struct ListNode* middleNode(struct ListNode* head) { struct ListNode* fast=head; struct ListNode* slow=head; while(fast&&fast->next) { fast=fast->next->next; slow=slow->next; } return slow; }
到此這篇關(guān)于C語(yǔ)言 超詳細(xì)介紹與實(shí)現(xiàn)線(xiàn)性表中的帶頭雙向循環(huán)鏈表的文章就介紹到這了,更多相關(guān)C語(yǔ)言 雙向循環(huán)鏈表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C語(yǔ)言詳解無(wú)頭單向非循環(huán)鏈表各種操作方法
- C語(yǔ)言詳解如何實(shí)現(xiàn)帶頭雙向循環(huán)鏈表
- C語(yǔ)言手把手帶你掌握帶頭雙向循環(huán)鏈表
- C語(yǔ)言超詳細(xì)講解數(shù)據(jù)結(jié)構(gòu)中雙向帶頭循環(huán)鏈表
- C語(yǔ)言?超詳細(xì)介紹與實(shí)現(xiàn)線(xiàn)性表中的無(wú)頭單向非循環(huán)鏈表
- C語(yǔ)言實(shí)現(xiàn)帶頭雙向循環(huán)鏈表
- C語(yǔ)言編程數(shù)據(jù)結(jié)構(gòu)帶頭雙向循環(huán)鏈表全面詳解
- C語(yǔ)言循環(huán)鏈表的原理與使用操作
相關(guān)文章
C/C++中接收return返回來(lái)的數(shù)組元素方法示例
return是C++預(yù)定義的語(yǔ)句,它提供了種植函數(shù)執(zhí)行的一種放大,最近學(xué)習(xí)中遇到了相關(guān)return的內(nèi)容,覺(jué)著有必要總結(jié)一下,這篇文章主要給大家介紹了關(guān)于C/C++中如何接收return返回來(lái)的數(shù)組元素的相關(guān)資料,需要的朋友可以參考下。2017-12-12C語(yǔ)言 詳細(xì)分析結(jié)構(gòu)體的內(nèi)存對(duì)齊
C 數(shù)組允許定義可存儲(chǔ)相同類(lèi)型數(shù)據(jù)項(xiàng)的變量,結(jié)構(gòu)是 C 編程中另一種用戶(hù)自定義的可用的數(shù)據(jù)類(lèi)型,它允許你存儲(chǔ)不同類(lèi)型的數(shù)據(jù)項(xiàng),本篇讓我們來(lái)了解C 的結(jié)構(gòu)體內(nèi)存對(duì)齊2022-03-03C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的通訊錄
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的通訊錄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02C語(yǔ)言實(shí)現(xiàn)計(jì)算樹(shù)的深度的方法
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)計(jì)算樹(shù)的深度的方法,針對(duì)數(shù)據(jù)結(jié)構(gòu)中樹(shù)進(jìn)行操作的方法,在算法設(shè)計(jì)中比較常見(jiàn),需要的朋友可以參考下2014-09-09C++中#pragma once與#ifndef對(duì)比分析
當(dāng)我們編寫(xiě)C++代碼時(shí),經(jīng)常需要使用頭文件來(lái)引入一些常用的函數(shù)、類(lèi)或者變量,如果一個(gè)頭文件被重復(fù)包含,就會(huì)導(dǎo)致編譯錯(cuò)誤或者運(yùn)行時(shí)錯(cuò),為了避免發(fā)生,我們需要使用預(yù)處理指令來(lái)防止頭文件被重復(fù)包含,常用的預(yù)處理指令有#pragma once和#ifndef,需要的朋友可以參考下2023-05-05內(nèi)聯(lián)函數(shù)inline與宏定義深入解析
類(lèi)的內(nèi)斂函數(shù)是一個(gè)真正的函數(shù)。使用內(nèi)聯(lián)函數(shù)inline可以完全取代表達(dá)式形式的宏定義2013-09-09C語(yǔ)言的getc()函數(shù)和gets()函數(shù)的使用對(duì)比
這篇文章主要介紹了C語(yǔ)言的getc()函數(shù)和gets()函數(shù)的使用對(duì)比,從數(shù)據(jù)流中一個(gè)是讀取字符一個(gè)是讀取字符串,需要的朋友可以參考下2015-08-08