數據結構篇其三---鏈表分類和雙向鏈表

?

前言

數據結構篇其二實現了一個簡單的單鏈表,鏈表的概念,單鏈表具體實現已經說明,如下:
單鏈表
事實上,前面的單鏈表本質上是無頭單向不循環鏈表。此篇說明的雙向鏈表可以說完全反過來了了。無論是之前的單鏈表還是雙向鏈表,本質都是鏈表家族的兩位成員。
主題一:鏈表分類
詳細說說鏈表的特征,以及這些特征組合的鏈表種類。
主題二:雙向鏈表的實現
像上次實現單鏈表一樣,這次也試著獨立實現雙向鏈表吧。
學習收獲:十分鐘手搓一個鏈表

為什么學習雙向鏈表?
因為雖然字面上雙向鏈表好像還難一點,結構雖然復雜,但是實現起來特別簡單。應用場景有顯著的優勢。

鏈表的分類

  • 單向與雙向

鏈表的單向與雙向:這說明節點與節點之間的聯系。單向鏈表節點的指針一路往后。雙向鏈表節點指針指前指后。
單向與雙向

可見,從定義上,雙向鏈表天生應該有兩個指針,所以在單鏈表的基礎上,我們可以推出雙向鏈表的定義。

//雙向鏈表的定義
typedef int LTDataType;
typedef struct ListNode {LTDataType data;//數據域struct ListNode* prev;//前驅指針struct ListNode* next;//后繼指針
}ListNode;

鏈表雙向和單向決定了它節點指針的數量

  • 帶頭與不帶頭

為了方便對鏈表進行操作,我們會在鏈表的第一個節點前附帶一個頭節點(哨兵位),注意頭節點不是第一個節點,第一個節點存儲的是有效數據。
頭節點的數據域不存儲有效的數據,指針域next指向第一個節點,若是雙向的話,則前驅指針指向尾結點。
需注意這個時候頭指針就指向頭節點,而不是第一個節點了。
單鏈表帶哨兵位

  • 循環非循環

若鏈表的尾結點指向頭節點而不是NULL,則鏈表閉合形成了一個環,可以循環了,就稱為循環鏈表。反之,則為不循環鏈表。
循環與非循環

  1. 以上就是鏈表的三大特征,每種特征又分兩種情況。組合起來一共8種,所以鏈表種類一共8種。
  2. 下面介紹雙向鏈表,來熟悉一下雙向,帶頭,循環的鏈表吧。

雙向鏈表的實現

下面實現這些函數

// 創建返回鏈表的頭結點.
ListNode* ListCreate();
// 雙向鏈表銷毀
void ListDestory(ListNode* plist);
// 雙向鏈表打印
void ListPrint(ListNode* plist);
// 雙向鏈表尾插
void ListPushBack(ListNode* plist, LTDataType x);
// 雙向鏈表尾刪
void ListPopBack(ListNode* plist);
// 雙向鏈表頭插
void ListPushFront(ListNode * plist, LTDataType x);
// 雙向鏈表頭刪
void ListPopFront(ListNode* plist);

前面已經給出了雙向鏈表的定義。但下圖只體現了雙向,循環和帶頭還要我們具體實現。

typedef int LTDataType;
typedef struct ListNode {LTDataType data;//數據域struct ListNode* prev;//前驅指針struct ListNode* next;//后繼指針
}ListNode;

開局一個頭指針,能這樣做嗎?


int main() {ListNode* plist = NULL;return 0;
}

這個是帶頭的鏈表,起碼有頭節點吧,所以先創建一個頭節點。
其次,這個是循環鏈表,頭節點的前驅指針和后繼指針應該都指向自己吧。
頭節點

節點創建

ListNode* ListCreate(LTDataType x) {ListNode* phead = (ListNode*)malloc(sizeof(ListNode));//判斷動態空間是否開辟失敗。if (phead == NULL) {perror("malloc fail");exit(-1);}phead->data = x;//頭節點數據隨便賦值phead->next = phead;//前驅,后繼指針指向自己phead->prev = phead;return phead;
}

雙向鏈表初始化

ListNode* ListInit() {return ListCreate(-1);//頭節點的數據域隨便給值。
}

還沒有元素啊,那就先插入節點吧

雙向鏈表的尾插

如何實現尾插呢?

  1. 保證每個節點的兩個指針有明確的指向。

  2. 尾插操作的節點有三個,頭節點,尾結點,新節點。

  3. 按照上面圖片的步驟寫代碼。

  4. 后面請自行畫圖分析,多創建臨時變量,良好的命名習慣。寫完一個函數去測試一下。

void ListPushBack(ListNode* phead, LTDataType x) {assert(phead);ListNode* newnode = ListCreate(x);//創建新節點ListNode* tail = phead->prev;//記錄尾結點//執行尾插操作phead->prev = newnode;newnode->next = phead;tail->next = newnode;newnode->prev = tail;
}

打印函數

void ListPrint(ListNode* phead) {assert(phead);ListNode* pcur = phead->next;printf("head->");while (pcur!= phead) {printf("%d->", pcur->data);pcur = pcur->next;}pcur = NULL;printf("return\n");
}

雙鏈表的頭插

void ListPushFront(ListNode* phead,LTDataType x) {assert(phead);ListNode* newnode = ListCreate(x);ListNode* first = phead->next;phead->next = newnode;newnode->prev = phead;newnode->next = first;first->prev = newnode;}

雙向鏈表的尾刪

//尾刪函數
void ListPopBack(ListNode* phead) {assert(phead);ListNode* tail = phead->prev;ListNode* tailprev = tail->prev;phead->prev = tailprev;tailprev->next = phead;if(phead->prev!=phead)//判斷是否為空表,哨兵位不能釋放了。free(tail);tail = NULL;}

雙向鏈表的頭刪

/頭刪函數
void ListPopFront(ListNode* phead) {assert(phead);ListNode* first = phead->next;ListNode* second = first->next;phead->next = first->next;second->prev = phead;if (phead != first)//哨兵位不能釋放{free(first);}first = NULL;second = NULL;
}

雙向鏈表的銷毀

void ListDestory(ListNode** pphead) {assert(pphead);assert(*pphead);ListNode* pcur = (*pphead)->next;ListNode* next = pcur->next;while (pcur != *pphead){free(pcur);pcur = next;next = next->next;}free(pcur);pcur = NULL;next = NULL;*pphead = NULL;}

雙向鏈表補充

// 雙向鏈表查找
ListNode* ListFind(ListNode* plist, LTDataType x);
// 雙向鏈表在pos的前面進行插入
void ListInsert(ListNode* pos, LTDataType x);
// 雙向鏈表刪除pos位置的節點
void ListErase(ListNode* pos);

雙向鏈表查找指定數據

ListNode* ListFind(ListNode* phead, LTDataType x) {assert(phead);ListNode* pcur = phead->next;while (pcur != phead) {if (pcur->data == x) {return pcur;//找到了返回當前節點的地址}pcur=pcur->next;}return NULL;//雙鏈表跑完一遍都沒找到,返回空。
}

在pos節點之前插入新節點

//在pos之前插入
void ListInsert(ListNode* pos, LTDataType x) {assert(pos);ListNode* newnode = ListCreate(x);ListNode* prev = pos->prev;newnode->next = pos;pos->prev = newnode;prev->next = newnode;newnode->prev = prev;pos = NULL;prev = NULL;newnode = NULL;
}

刪除位置為pos的節點

void ListErase(ListNode* pos) {assert(pos);ListNode* prev = pos->prev;ListNode* next = pos->next;free(pos);prev->next = next;next->prev = prev;pos = NULL;prev = NULL;next = NULL;
}

十分鐘實現一個鏈表

  1. 實現什么類型的鏈表?
  2. 需要寫什么函數?

雙向鏈表;
實現函數,增刪查改,還有來鏈表的初始化,銷毀。


#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int LTDataType;
typedef struct ListNode {LTDataType data;struct ListNode* next;struct ListNode* prev;
}LT;LT* LTInit(void) {LT* newnode = (LT*)malloc(sizeof(LT));if (newnode == NULL) {perror("malloc fail");exit(-1);}newnode->next = newnode;newnode->prev = newnode;return newnode;
}LT LTDestory(LT** pphead) {assert(pphead);assert(*pphead);LT* pcur = (*pphead)->next;LT* next = pcur->next;while (pcur != *pphead) {free(pcur);pcur = next;next = next->next;}free(pcur);*pphead = NULL;}//在pos之前插入新節點
void LTInsert(LT* pos, LTDataType x) {assert(pos);LT* prev = pos->prev;LT * newnode = (LT*)malloc(sizeof(LT));if (newnode == NULL) {perror("malloc fail");exit(-1);}newnode->data = x;prev->next = newnode;newnode->prev = prev;newnode->next = pos;pos->prev = newnode;}void LTErase(LT* pos) {assert(pos);LT* prev = pos->prev;LT* next = pos->next;free(pos);prev->next = next;next->prev = prev;
}void LTPushBack(LT* phead,LTDataType x) {LTInsert(phead, x);
}void LTPushFront(LT* phead, LTDataType x) {LTInsert(phead->next, x);
}void LTPopBack(LT* phead) {LTErase(phead->prev);
}void LTPopFront(LT* phead) {LTErase(phead->next);
}LT* LTFind(LT* phead, LTDataType x) {LT* pcur = phead->next;while (pcur != phead) {if (pcur->data == x) {return pcur;}pcur = pcur->next;}return NULL;return;
}LT* LTMidfy( LT* pos,LTDataType x) {pos->data = x;
}

雙向鏈表完結。鏈表完結!

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

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

相關文章

Java進階學習筆記12——final、常量

final關鍵字&#xff1a; final是最終的意思。可以修飾類、方法、變量。 修飾類&#xff1a;該類就被稱為最終類&#xff0c;特點是不能被繼承了。 修飾方法&#xff1a;該方法是最終方法&#xff0c;特點是不能被重寫了。 修飾變量&#xff1a;該變量只能被賦值一次。 有些…

智慧校園的建設思路

智慧校園建設的一個主要目的就是要打破學校內的信息孤島&#xff0c;其核心是在人、流程和信息三個層面的全面整合。智慧校園應該能夠為全校師生員工及校外用戶提供統一的、一站式的服務渠道&#xff1b;能夠將學校各種業務流程連接起來&#xff0c;實現各種應用系統的互聯互通…

postgresql insert on conflict 不存在則插入,存在則更新

向一張表執行插入動作&#xff0c;如果插入的字段數據已存在&#xff0c;則執行更新操作&#xff0c;不存在則進行插入操作。 1、創建一張表 CREATE TABLE "user_info" ( "id" int2 NOT NULL, "name" varchar(20) COLLATE "pg_catalog&quo…

基于Tensorflow卷積神經網絡人臉識別公寓人員進出管理系統

歡迎大家點贊、收藏、關注、評論啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代碼。 文章目錄 一項目簡介 二、功能三、系統四. 總結 一項目簡介 一、項目背景與意義 隨著科技的快速發展和智能化水平的提高&#xff0c;公寓管理面臨著越來越多的挑戰。傳統的公寓…

Go語言標準庫之log和三方庫zap

一、Log 1.1 logger基本使用 Go語言內置的log包實現了簡單的日志服務。本包也提供了一個預定義的“標準”logger&#xff0c;可以通過調用函數Print系列(Print|Printf|Println)、Fatal系列&#xff08;Fatal|Fatalf|Fatalln)、和Panic系列&#xff08;Panic|Panicf|Panicln)來…

C++ 數據結構算法 學習筆記(32) -五大排序算法

C 數據結構算法 學習筆記(32) -五大排序算法 選擇算法 如下若有多個女生的身高需要做排序: 常規思維: 第一步先找出所有候選美女中身高最高的&#xff0c;與最后一個數交換 第二步再找出除最后一位美女外其它美女中的最高者&#xff0c;與倒數第二個美女交換位置 再找出除最…

k8s-pod詳解

一、Pod基本概念&#xff1a; 1.pod介紹&#xff1a; Pod是kubernetes中最小的資源管理組件&#xff0c;Pod也是最小化運行容器化應用的資源對象。一個Pod代表著集群中運行的一個進程。kubernetes中其他大多數組件都是圍繞著Pod來進行支撐和擴展Pod功能的&#xff0c;例如&am…

電賽經驗分享——賽前準備

? 大家好哇&#xff01;我是小光&#xff0c;想要成為系統架構師的嵌入式愛好者。 ?在之前的電賽中取得了省一的成績&#xff0c;本文對電賽比賽前需要準備什么做一個經驗分享。 ?感謝你的閱讀&#xff0c;不對的地方歡迎指正。 加入小光嵌入式交流群&#xff08;qq群號&…

在線人才測評在企業招聘和大學生求職中的應用場景

每年的春招秋招&#xff0c;都是畢業生們忙著找工作的季節&#xff0c;相比社招來說&#xff0c;春招秋招是每個畢業生務必重視的機會&#xff0c;大廠名企畢竟名額有限&#xff0c;如果找到自己心儀的職業崗位&#xff0c;作為畢業生就必須提前準備&#xff0c;深入了解招聘的…

五管OTA輸入極性快速判斷

做CMFB還有負反饋的時候曾經在判斷輸入輸出極性上吃了大虧&#xff0c;直接做實驗波形正確就是輸入正端&#xff0c;全差分就不用考慮這么多了 和彎折&#xff0c;形狀類似7&#xff0c;相同方向輸入正端&#xff0c;相反的就是輸入負端&#xff0c;輸出也是和輸入負端一個方向…

【NLP】人機對話

概念 機器翻譯就是用計算機把一種語言翻譯成另外一種語言的技術 機器翻譯的產生與發展 17 世紀&#xff0c;笛卡爾與萊布尼茨試圖用統一的數字代碼來編寫詞典 1930 機器腦 1933 蘇聯發明家特洛陽斯基用機械方法將一種語言翻譯為另一種語言 1946 ENIAC 誕生 1949 機器翻譯問題…

香蕉成熟度檢測YOLOV8NANO

香蕉成熟度檢測YOLOV8NANO&#xff0c;采用YOLOV8NANO訓練&#xff0c;得到PT模型&#xff0c;然后轉換成ONNX模型&#xff0c;讓OEPNCV調用&#xff0c;從而擺脫PYTORCH依賴&#xff0c;支持C。python&#xff0c;安卓開發。能檢測六種香蕉類型freshripe freshunripe overripe…

Vita-CLIP: Video and text adaptive CLIP via Multimodal Prompting

標題&#xff1a;Vita-CLIP: 通過多模態提示進行視頻和文本自適應CLIP 源文鏈接&#xff1a;https://openaccess.thecvf.com/content/CVPR2023/papers/Wasim_Vita-CLIP_Video_and_Text_Adaptive_CLIP_via_Multimodal_Prompting_CVPR_2023_paper.pdfhttps://openaccess.thecvf.…

sw布爾減

可能最有效率還是草圖邊界線,然后用草圖做分割

ue5 中ps使用記錄貼

一、快捷鍵記錄 放大圖形 ctrlalt空格 放大圖形 縮小視口 ctrl空格 ctrlD 取消選區 ctrlt縮小文字 w魔棒工具 選擇魔棒的時候把容差打開的多一點 二、案例 移動文字 在相應的圖層選擇 移動文字 修改圖片里的顏色 在通道里拷貝紅色通道&#xff0c;復制紅色通道粘貼給正常圖…

記錄Hbase出現HMaster一直初始化,日志打印hbase:meta,,1.1588230740 is NOT online問題的解決

具體錯誤 hbase:meta,,1.1588230740 is NOT online&#xff1b; state{1588230740 stateOPEN, ...... 使用 hbase 2.5.5 &#xff0c;hdfs和hbase分離兩臺服務器。 總過程 1. 問題發現 在使用HBase的程序發出無法進行插入到HBase操作日志后檢查HBase狀況。發現master節點和r…

大模型應用商業化落地關鍵:給企業帶來真實的業務價值

2024 年被很多人稱為大模型應用的元年&#xff0c;毫無疑問&#xff0c;大模型已經成為共識&#xff0c;下一步更急迫的問題也擺在了大家的面前——大模型到底能夠用在哪&#xff1f;有哪些場景能落地&#xff1f;怎么做才能創造真正的價值&#xff1f; 在剛剛過去的 AICon 全…

【排序算法】快速排序(四個版本以及兩種優化)含動圖)

制作不易&#xff0c;三連支持一下吧&#xff01;&#xff01;&#xff01; 文章目錄 前言一.快速排序Hoare版本實現二.快速排序挖坑法版本實現三.快速排序前后指針版本實現四.快速排序的非遞歸版本實現五.兩種優化總結 前言 前兩篇博客介紹了插入和選擇排序&#xff0c;這篇博…

halcon配合yolov8導出onnx模型檢測物體

1.工業上多數視覺開發都是用halcon開發的&#xff0c;halcon本身也有自己的深度學習網絡&#xff0c;至于halcon如果不使用本身的深度學習&#xff0c;使用其他網絡導出的onnx模型怎樣配合使用&#xff1f;本文基于yolov8寫一個列子。 2。創建輸入數據的轉換代碼 #region 創建輸…

Nginx高可用性架構:實現負載均衡與故障轉移的探索

隨著網絡應用的不斷發展和用戶訪問量的增長&#xff0c;如何確保系統的高可用性、實現負載均衡以及快速響應故障轉移成為了每個運維和開發團隊必須面對的挑戰。Nginx作為一款高性能的HTTP和反向代理服務器&#xff0c;憑借其強大的功能和靈活的配置&#xff0c;成為了實現這些目…