C語言數據結構基礎——雙鏈表專題

前言

? ?書接上回,雙鏈表便是集齊帶頭、雙向、循環等幾乎所有元素的單鏈表PLUS.

1.初始化、創建雙鏈表

typedef int LTDataType;
typedef struct LTNode {LTDataType data;struct LTNode* next;struct LTNode* prev;
}LTNode;

? ?不同于單鏈表,此時每個節點應當包含兩個指針,一個指向前,一個指向后。

任然將創建節點和初始化雙鏈表封裝成兩個函數

LTNode* LTBuyNode(LTDataType x) {LTNode* phead = (LTNode*)malloc(sizeof(LTNode));if (phead == NULL) {perror("malloc fail!");exit(1);}phead->data = x;phead->next = phead->prev = NULL;
}LTNode* LTInit() {LTNode* phead = LTBuyNode(-1);phead->next = phead;phead->prev = phead;return phead;
}

? ? ? LTInit步驟中,phead便是我們的哨兵位,可以不予其data賦值,也可以賦予一個不太可能成為數據的值。但是我們需要將他的next指針和prev指針分別指向下一個節點(目前是他自己)和上一個節點(目前也是他自己),這樣就形成了雙鏈表的雛形

2.插入接口

2.1尾插

void LTNodePushBack(LTNode* phead,LTDataType x) {assert(phead);LTNode* newnode = LTBuyNode(x);//開始調整各個指針指向phead->next = newnode;
}

請各位稍加思考,開始調整指針的第一句對嗎?

錯了!我們認為此時的雙鏈表只有一個頭結點,所以尾差應該插在哨兵位后面,但我們函數的目的是適用于所有的尾插,這便是慣性思維?帶來的錯誤。寫各種功能函數時,提前構思出各種情況固然是好事,但對于我們新手與初學者而言,先在腦海中的普通且簡答的情況下寫出接口,再根據各個特殊情況調整才更加合適。

那我們還需要遍歷鏈表找尾節點嗎?

答案是否定的,由于循環鏈表的緣故,我們可以從頭結點(哨兵位)找到現在的尾節點,也就是phead->prev

void LTNodePushBack(LTNode* phead,LTDataType x) {assert(phead);LTNode* newnode = LTBuyNode(x);//開始調整各個指針指向newnode->prev = phead->prev;newnode->next = phead;//先修改新節點的元素的指向,此時不會導致任何節點丟失phead->prev->next = newnode;phead->prev = newnode;
}

打印函數封裝如下:

void LTNodePrint(LTNode* phead) {assert(phead);LTNode* pcur = phead->next;while (pcur != phead) {printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}

不同于前面的單鏈表,此處我們沒有再使用二級指針,原因如下:

當鏈表中只有哨兵位節點時,我們稱鏈表為空鏈表,無論如何,我們不應該刪除的哨兵位。

所以,不同于單鏈表,雙鏈表一般情況不需要傳二級指針

? ? ? ?單鏈表很多時候設計修改自己的地址,所以需要使用二級指針,而雙鏈表大多數可以直接通過一級指針修改指針指向的內容,不需要使用二級指針。

但比如說,實現刪除鏈表的接口,此時就可以哨兵位的二級指針,因為涉及到修改、刪除哨兵位。

不過一級指針也可以使用(只是最后需要手動置NULL),但是可以保證接口一致性,接口一致性能降低客戶的使用成本。


再補充一個博主修改雙鏈表指針指向的思路:

1.首先修改要插入節點的本身元素指針的指向。

2.再修改待插入元素的前驅和后驅的指針指向。


2.2頭插

? ? 頭插是在第一個有效節點之前插入數據,而不是在哨兵位之前插入。哨兵位之前插入數據和尾差無異。尾差才是在最后一個有效節點之后插入數據/哨兵位之前插入數據

void LTNodePushInfront(LTNode* phead, LTDataType x) {assert(phead);LTNode* newnode = LTBuyNode(x);newnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}

賦值思路依然如上:先給newnode的next和prev賦值,此時這樣操作不會影響任何人,再依次改變前驅和后驅節點的指針指向

3.刪除接口

3.1尾刪

除了斷言哨兵位是否為空,還要斷言phead->next!=phead(只剩一個哨兵位也叫空鏈表,不能再進行刪除操作),頭刪也是這個道理。

void LTNodePopBack(LTNode* phead) {assert(phead);assert(phead != phead->next);LTNode* ptail = phead->prev;ptail->prev->next = phead;phead->prev = ptail -> prev;free(ptail);
}

感覺到指針指向較多怕丟失時,也可以像上面這樣定義一個新變量記錄地址,也更加容易理解。

3.2頭刪

? ? 同理,為了不造成空間浪費,我們仍然定義一個新變量來記錄想刪除的第一個有效節點,方便使用free函數。

void LTNodePopInfront(LTNode* phead) {assert(phead);assert(phead != phead->next);phead->next->next->prev = phead;LTNode* del = phead->next;phead->next = phead->next->next;free(del);del = NULL;
}

4.指定位置的操作

4.1查找接口

? ? 為了便于獲得指定位置的操作的實參,我們實現一個查找函數。

LTNode* LTFind(LTNode* phead, LTDataType x) {assert(phead);LTNode* pcur = phead->next;for (; pcur != phead; pcur = pcur->next) {if (pcur->data == x) {return pcur;}}printf("find LTData Failed!");return NULL;
}

4.3指定位置之后插入數據

void LTInsert(LTNode* pos, LTDataType x) {assert(pos);LTNode* newnode = LTBuyNode(x);newnode->prev = pos;newnode->next = pos->next;//先完成newnode的賦值pos->next->prev = newnode;pos->next = newnode;
}

4.4刪除指定位置的節點?

理不清楚關系就定義新變量,思路一下就簡化了

void LTErase(LTNode* pos) {assert(pos);LTNode* prev = pos->prev;LTNode* next = pos->next;prev->next = next;next->prev = prev;free(pos);
}

? ? 最后全部的測試的通過了。

5.鏈表與順序表的比較和數據結構小結

我們已經學習了兩種類型的數據結構,下面進行小結

數據結構是與數據庫/文件等價的一門課程,在高校中這兩種管理方式也多以單獨的課程開放。

那么就我們學習過的順序表和鏈表兩種結構而言,孰優孰劣呢?

順序表:

(所謂隨機訪問并不是真的表示隨機,而是說我想訪問哪都可以直接訪問)

鏈表(一般不說單鏈表,而說功能齊全的雙鏈表)

就紅字內容,我們再稍微簡略的展開說說:

cpu是不會直接從內存中拿取數據的(速度:寄存器>緩存>內存>硬盤),一般情況都是從緩存中拿取數據(數據量小的時候寄存器也可以直接拿數據)。

大部分情況下,如果緩存中有數據,cpu就可以直接“命中”,沒有就不命中,先從內存加載到緩存中再命中。

由局部性原理,cpu會一次性的直接去拿一定體量的連續數據(由硬件性質決定)。

而由于順序表是連續的,比如下圖,第一次沒能命中,由于已經加載了沒有命中的指針所指向的數據及其后面空間的數據,之后都能直接命中,而對于空間不連續的鏈表,大概率情況下是不會繼續命中的,每一次都會經歷從內存加載到緩存的過程,降低效率。甚至有可能造成緩存污染,也cpu一次性能裝的數據有限,很多有用的數據可能被無效的節點之后的空間擠掉,造成污染。

過程如下:

(cache line為緩存)

在cache line的話直接命中,較高效

不在的話就不命中,去內存中找(比如順序表是連續的內存,就會很方便找)。

6.小結

存在即合理,在當順序表和鏈表沒有其他接口的影響時,順序表的查找會更快。

充分的理解各種數據結構,手撕各種數據結構才能在以后的學習中更方便選型。可參考:

與程序員相關的CPU緩存知識 | 酷 殼 - CoolShell

??

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/714619.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/714619.shtml
英文地址,請注明出處:http://en.pswp.cn/news/714619.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

selenium初始學習--打開新標簽操作

selenium 打開新標簽操作 簡單說一下使用 環境 :python 3.9 selenium 4,18 初始化操作 目的 打開bilibilie網站并搜索視頻(電影) 并點擊觀看 操作 打開應用并搜索網址 from selenium import webdriver import timefrom selenium.webdr…

PySide6+VSCode Python可視化環境搭建

#記住在cmd中運行,不要在vscode里運行,否則env會裝到工程目錄下 python -m venv env #env\Scripts\activate.bat pip install pyside6 下載本期源碼 vscode裝一個PYQT Integration插件,設置好兩個路徑(下面有個腳本用于獲取路徑&…

MySQL 數據庫表設計和優化

一、數據結構設計 正確的數據結構設計對數據庫的性能是非常重要的。 在設計數據表時,盡量遵循一下幾點: 將數據分解為合適的表,每個表都應該有清晰定義的目的,避免將過多的數據存儲在單個表中。使用適當的數據類型來存儲數據&…

2020小學甲組--恢復數組

題目描述 有一個數組a[1..n]&#xff0c;但是這個數組的內容丟失了&#xff0c;你要嘗試恢復它。已知以下的三個事實&#xff1a; 1、對于1<i<n&#xff0c;都有a[i]>0&#xff0c;且所有的a[i]互不相同。即a數組保存的全部都是正整數&#xff0c;且互不相同。 2、…

挑戰杯 基于機器視覺的車道線檢測

文章目錄 1 前言2 先上成果3 車道線4 問題抽象(建立模型)5 幀掩碼(Frame Mask)6 車道檢測的圖像預處理7 圖像閾值化8 霍夫線變換9 實現車道檢測9.1 幀掩碼創建9.2 圖像預處理9.2.1 圖像閾值化9.2.2 霍夫線變換 最后 1 前言 &#x1f525; 優質競賽項目系列&#xff0c;今天要分…

范偉:你們怎么老提1,200呢,有什么典故啊?趙本山:沒有啊!

范偉&#xff1a;你們怎么老提1,200呢,有什么典故啊?趙本山&#xff1a;沒有啊&#xff01; --小品《面子》&#xff08;中3&#xff09;的臺詞 表演者&#xff1a;趙本山 高秀敏 范偉 &#xff08;接上&#xff09; 范偉&#xff1a;哎吃啊 趙&#xff1a;哎呀這電視看的挺…

Acwing枚舉、模擬與排序(一)

連號區間數 原題鏈接&#xff1a;https://www.acwing.com/problem/content/1212/ 初始最小值和最大值的依據是題目給出的數據范圍。只要在數據范圍之外就可以。 連號的時候&#xff0c;相鄰元素元素之間&#xff0c;差值為1。那么區間右邊界和左邊界&#xff0c;的值的差&#…

cAdvisor+Prometheus+Grafana 搞定Docker容器監控平臺

cAdvisorPrometheusGrafana cAdvisorPrometheusGrafana 搞定Docker容器監控平臺1、先給虛擬機上傳cadvisor2、What is Prometheus?2.1、架構圖 3、利用docker安裝普羅米修斯4、安裝grafana cAdvisorPrometheusGrafana 搞定Docker容器監控平臺 1、先給虛擬機上傳cadvisor cAd…

MySQL事務和鎖機制

MySQL技術——事務和鎖機制 一、事務&#xff08;1&#xff09;概述&#xff08;2&#xff09;ACID特性&#xff08;3&#xff09;事務并發存在的問題&#xff08;4&#xff09;事務的隔離級別 二、鎖機制&#xff08;1&#xff09;鎖的力度&#xff08;2&#xff09;表的分類&…

網絡編程-編碼與解碼(Protobuf)

編碼與解碼 下面的文字都來自于極客時間 為什么要編解碼呢&#xff1f;因為計算機數據傳輸的是二進制的字節數據 解碼&#xff1a;字節數據 --> 字符串&#xff08;字符數據&#xff09; 編碼&#xff1a;字符串&#xff08;字符數據&#xff09;–> 字節數據 我們在編…

Python 實現海康機器人工業相機 MV-CS050-10GC 的實時顯示視頻流及拍照功能(實時顯示視頻流同時可以進行拍照)

參考鏈接&#xff1a; https://www.cnblogs.com/HanYork/p/17388506.html https://www.cnblogs.com/miracle-luna/p/16960556.html#5138211 Flask搭建流媒體服務器&#xff1a;使用Flask搭建一個流媒體服務器_multipart/x-mixed-replace; boundaryframe-CSDN博客

公共字段自動填充

在開發中經常面臨對于一些公共字段的賦值。 如在下表中&#xff1a; 如何讓程序自動為我們需要賦值的公共字段進行賦值&#xff0c;避免在業務代碼中重復寫這些公共字段的賦值代碼 如下圖所示&#xff1a; 實現思路&#xff1a; 1.自定義注解AutoFill&#xff0c;用于標識需…

linux環境安裝cuda toolkit

1 全新安裝 如果環境中沒安裝過cuda版本&#xff0c; 這種情況下比較簡單。 直接在https://developer.nvidia.com/cuda-toolkit-archive選擇對應版本下載安裝即可。 如下為安裝cuda toolkit 11.8. 2 環境中已經存在其他版本 這種情況下比較復雜一些。 首先要確認最高支持的…

李沐動手學習深度學習——4.2練習

1. 在所有其他參數保持不變的情況下&#xff0c;更改超參數num_hiddens的值&#xff0c;并查看此超參數的變化對結果有何影響。確定此超參數的最佳值。 通過改變隱藏層的數量&#xff0c;導致就是函數擬合復雜度下降&#xff0c;隱藏層過多可能導致過擬合&#xff0c;而過少導…

Git多人合作的推送流程

多人合作時&#xff0c;使用Git進行代碼推動&#xff08;push&#xff09;需要一定的協調和規范&#xff0c;以確保代碼庫的整體健康。以下是一個常見的多人合作時的Git代碼推動流程&#xff1a; 同步主分支&#xff1a; 在推送之前&#xff0c;確保你的本地主分支&#xff08;…

【Java】四大函數式接口

消費型接口Consumer 消費型接口接收一個輸入&#xff0c;沒有返回值 在stream流計算中 forEach() 接收一個消費型接口Consumer用于 遍歷元素 /*** 消費型接口* 接收一個輸入&#xff0c;沒有返回值*/ public class demo01 {public static void main(String[] args) {//TODO 消…

【MySQL】表的內連和外連(重點)

表的連接分為內連和外連。 一、內連接 內連接實際上就是利用 where 子句對兩種表形成的笛卡兒積進行篩選&#xff0c;前面學習的查詢都是內連接&#xff0c;也是在開發過程中使用的最多的連接查詢。 select 字段 from 表1 inner join 表2 on 連接條件 and 其他條件; 注意&…

【數倉】Hadoop集群配置常用參數說明

Hadoop集群中&#xff0c;需要配置的文件主要包括四個 配置核心Hadoop參數&#xff1a; 編輯core-site.xml文件&#xff0c;設置Hadoop集群的基本參數&#xff0c;如文件系統、Hadoop臨時目錄等。 配置HDFS參數&#xff1a; 編輯hdfs-site.xml文件&#xff0c;設置HDFS的相關參…

策略開發:EMA如何計算

EMA的計算原理 EMA 是MA&#xff08;平滑移動平均線&#xff09;的另一種形式。全名“加權指數移動平均線”。 2/13就是12日移動平均線的平滑因子&#xff0c;他的意思是指&#xff1a;給予新價格 2/13的權重&#xff0c;給予過去的EMA 11/13的權重。 在計算的時候第一天的M…

Linux使用基礎命令

1.常用系統工作命令 (1).用echo命令查看SHELL變量的值 qiangziqiangzi-virtual-machine:~$ echo $SHELL /bin/bash(2).查看本機主機名 qiangziqiangzi-virtual-machine:~$ echo $HOSTNAME qiangzi-virtual-machine (3).date命令用于顯示/設置系統的時間或日期 qiangziqian…