【數據結構】--- 雙向鏈表的增刪查改

前言:

? ? ? 經過了幾個月的漫長歲月,回頭時年邁的小編發現,數據結構的內容還沒有寫博客,于是小編趕緊停下手頭的活動,補上博客以洗清身上的罪孽


目錄

? ? ? ??前言:

? ? ? ? ? ? ? ? ? ?概念:

? ? ? ? ?雙鏈表的初始化

? ? ? ? ?雙鏈表的判空

? ? ? ? ?雙鏈表的打印

? ? ? ? ??雙鏈表的頭插

? ? ? ? ??雙鏈表的尾插

? ? ? ? ? ?雙鏈表頭刪

? ? ? ? ?雙鏈表的尾刪

? ? ? ? ??雙鏈表的查找

? ? ? ? ?雙鏈表在pos位置前進行插入

? ? ? ??雙鏈表刪除pos位置

? ? ? ? ?雙鏈表的銷毀

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??檢查:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?完整代碼:

????????????????????????????????????????dlist.h

??????????????????????????????????????????????????????dlist.c

?????????????????????????????????????????????????????????????test.c? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

總結:


概念:

? ? ? 雙向鏈表的英文是?Doubly Linked List。雙向鏈表是一種鏈表數據結構,其中每個節點包含三個部分:前驅指針、數據域和后繼指針。前驅指針指向前一個節點,后繼指針指向下一個節點。

雙向鏈表的節點結構

雙向鏈表的節點結構包括三個部分:

  1. 前驅指針域 (_prev):用于存放指向上一個節點的指針。

  2. 數據域 (_data):用于存儲節點的數據元素。

  3. 后繼指針域 (_next):用于存放指向下一個節點的指針。

typedef int LTDataType;
typedef struct ListNode
{LTDataType _data;struct ListNode* _next;struct ListNode* _prev;
}ListNode;

雙鏈表的基本操作

? ? ???雙鏈表是一種數據結構,它的每個節點除了存儲數據外,還有兩個指針,分別指向前一個節點和后一個節點。這種結構使得雙鏈表在進行插入和刪除操作時更為高效,因為可以直接訪問任何節點的前驅和后繼節點。

雙鏈表的初始化

? ? ? 在開始對我們的雙鏈表進行增刪查改前,我們先要對鏈表進行初始化,和單鏈表差不多,需要一個創建新節點的函數,不同的是新節點多了一個前驅,也需要置空

? ? ? 然后創建鏈表的頭節點,頭節點的值我們取-1,當頭節點創建成功的時候,我們將其前驅和后繼指向自己

//創建新節點
ListNode* BuySListNode(LTDataType x) {ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL) {perror("malloc fail");return NULL;}newnode->_data = x;newnode->_next = NULL;newnode->_prev= NULL;return newnode;
}
// 創建返回鏈表的頭結點.
ListNode* ListCreate() {ListNode*head= BuySListNode(-1);if (head != NULL) {head->_prev = head;head->_next = head;}return head;
}
雙鏈表的判空
bool ListEmptyLTNode(ListNode* phead)
{assert(phead);/*鏈表返回只剩頭節點(鏈表已經被刪空)為真否則為假*/return phead->_next == phead;
}
雙鏈表的打印

遍歷鏈表

// 雙向鏈表打印
void ListPrint(ListNode* pHead) {assert(pHead);ListNode* start = pHead->_next;while (start!=pHead) {printf("%d<=>", start->_data);start = start->_next;}printf("\n");
}
雙鏈表的頭插

? ? ? ?創建一個新節點,保存頭節點的后繼,令其為ne,讓新節點的下一個指向ne,ne的前驅指向新節點,新節點的前驅指向頭節點,頭節點的后繼指向新節點

// 雙向鏈表頭插
void ListPushFront(ListNode* pHead, LTDataType x) {assert(pHead);ListNode* newnode = BuySListNode(x);ListNode* ne = pHead->_next;newnode->_next = ne;ne->_prev = newnode;newnode->_prev = pHead;pHead->_next = newnode;
}
雙鏈表的尾插
// 在鏈表尾部插入新節點
void ListPushBack(ListNode* pHead, LTDataType x) {assert(pHead);ListNode* newnode = BuySListNode(x);  // 創建新節點// 調整指針將新節點接入鏈表尾部ListNode* pre = pHead->_prev;  // 原尾節點pre->_next = newnode;          // 原尾節點的next指向新節點newnode->_prev = pre;          // 新節點的prev指向原尾節點newnode->_next = pHead;        // 新節點的next指向頭節點pHead->_prev = newnode;        // 頭節點的prev指向新尾節點
}

雙鏈表頭刪

先看看鏈表是否為空,空鏈表不能進行尾刪

? ? ? 然后保存頭節點的后繼的后繼,讓頭節點的后繼指向頭節點的后繼的后繼,頭節點的后繼的后繼的前驅指向頭節點

// 雙向鏈表頭刪
void ListPopFront(ListNode* pHead) {assert(pHead);if (!ListEmptyLTNode(pHead)) {ListNode* check = pHead->_next->_next;ListNode* tmp = pHead->_next;pHead->_next = check;check->_prev = pHead;free(tmp);}
}
雙鏈表的尾刪

先看看鏈表是否為空,空鏈表不能進行尾刪

? ? ? 然后保存頭節點的前驅的前驅,讓頭節點的前驅指向頭節點的前驅的前驅,頭節點的前驅的前驅的后繼指向頭節點

// 雙向鏈表尾刪
void ListPopBack(ListNode* pHead) {assert(pHead);if (!ListEmptyLTNode(pHead)) {ListNode* tmp = pHead->_prev;ListNode* pre = pHead->_prev->_prev;pre->_next = pHead;pHead->_prev = pre;free(tmp);}}
雙鏈表的查找

遍歷鏈表

// 雙向鏈表查找
ListNode* ListFind(ListNode* pHead, LTDataType x) {assert(pHead);ListNode* start = pHead->_next;while (start != pHead) {if (start->_data == x)return start;start = start->_next;}return NULL;
}
雙鏈表在pos位置前進行插入

? ? ? ?先判斷pos是否有效,創建一個新節點,然后將pos位置的前驅保存下來,讓新節點的前驅指向pos的前驅新節點的后繼指向pos,pos的前驅指向新節點,pos前驅的后繼指向新節點

// 雙向鏈表在pos的前面進行插入
void ListInsert(ListNode* pos, LTDataType x) {assert(pos);ListNode* newnode = BuySListNode(x);ListNode* pre = pos->_prev;newnode->_next = pos;pos->_prev = newnode;pre->_next = newnode;newnode->_prev = pre;
}
雙鏈表刪除pos位置

? ? ? ?先判斷pos是否有效,然后將pos位置的前驅和后繼保存下來,讓pos的前驅的后繼指向pos的后繼,pos后繼的前驅指向pos的前驅

// 雙向鏈表刪除pos位置的節點
void ListErase(ListNode* pos) {assert(pos);ListNode* ne = pos->_next;ListNode* pre = pos->_prev;ne->_prev = pre;pre->_next = ne;free(pos);
}
雙鏈表的銷毀

? ? ?先斷言頭節點有效,然后再創建一個start指針指向頭節點的后繼,當該指針不等于頭節點時,不斷遍歷,先保存start指針的后繼,然后釋放掉start指針所指向的內存,再將原來保存的后繼重新給到start指針,最后再釋放頭節點,需在外層置空指針,防止野指針問題。

// 雙向鏈表銷毀
void ListDestory(ListNode* pHead) {assert(pHead);ListNode* start = pHead->_next;while (start != pHead) {ListNode* ne = start->_next;free(start);start = ne;}free(pHead);//pHead = NULL;
}
檢查:
?
#include "dlist.h"int main() {ListNode* head = ListCreate(); // 創建鏈表頭節點// 尾插入測試ListPushBack(head, 1);ListPushBack(head, 2);ListPushBack(head, 3);printf("鏈表內容(尾插入后):");ListPrint(head);// 頭插入測試ListPushFront(head, 0);printf("鏈表內容(頭插入后):");ListPrint(head);// 查找測試ListNode* found = ListFind(head, 2);if (found) {printf("找到節點:%d\n", found->_data);}else {printf("未找到節點\n");}// 刪除頭節點測試ListPopFront(head);printf("鏈表內容(頭刪后):");ListPrint(head);// 刪除尾節點測試ListPopBack(head);printf("鏈表內容(尾刪后):");ListPrint(head);// 在指定位置插入測試ListInsert(head->_next, 4); // 在第二個節點后插入printf("鏈表內容(插入4后):");ListPrint(head);// 刪除指定節點測試ListErase(head->_next); // 刪除第二個節點printf("鏈表內容(刪除第二個節點后):");ListPrint(head);// 銷毀鏈表ListDestory(head);// printf("鏈表內容(銷毀后):");// ListPrint(head); // 應該輸出空鏈表的狀態
head=NULL;return 0;
}
完整代碼:
dlist.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
// 帶頭+雙向+循環鏈表增刪查改實現
typedef int LTDataType;
typedef struct ListNode
{LTDataType _data;struct ListNode* _next;struct ListNode* _prev;
}ListNode;ListNode* BuySListNode(LTDataType x);
// 創建返回鏈表的頭結點.
ListNode* ListCreate();
// 雙向鏈表銷毀
void ListDestory(ListNode* pHead);
// 雙向鏈表打印
void ListPrint(ListNode* pHead);
// 雙向鏈表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 雙向鏈表尾刪
void ListPopBack(ListNode* pHead);
// 雙向鏈表頭插
void ListPushFront(ListNode* pHead, LTDataType x);
// 雙向鏈表頭刪
void ListPopFront(ListNode* pHead);
// 雙向鏈表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 雙向鏈表在pos的前面進行插入
void ListInsert(ListNode* pos, LTDataType x);
// 雙向鏈表刪除pos位置的節點
void ListErase(ListNode* pos);
bool ListEmptyLTNode(ListNode* phead);
dlist.c
#include "dlist.h"ListNode* BuySListNode(LTDataType x) {ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL) {perror("malloc fail");return NULL;}newnode->_data = x;newnode->_next = NULL;newnode->_prev= NULL;return newnode;
}
// 創建返回鏈表的頭結點.
ListNode* ListCreate() {ListNode*head= BuySListNode(-1);if (head != NULL) {head->_prev = head;head->_next = head;}return head;
}
// 雙向鏈表銷毀
void ListDestory(ListNode* pHead) {assert(pHead);ListNode* start = pHead->_next;while (start != pHead) {ListNode* ne = start->_next;free(start);start = ne;}free(pHead);//pHead = NULL;
}
bool ListEmptyLTNode(ListNode* phead)
{assert(phead);/*鏈表返回只剩頭節點(鏈表已經被刪空)為真否則為假*/return phead->_next == phead;
}
// 雙向鏈表打印
void ListPrint(ListNode* pHead) {assert(pHead);ListNode* start = pHead->_next;while (start!=pHead) {printf("%d<=>", start->_data);start = start->_next;}printf("\n");
}
// 雙向鏈表尾插
void ListPushBack(ListNode* pHead, LTDataType x) {assert(pHead);ListNode* newnode = BuySListNode(x);ListNode* pre = pHead->_prev;pre->_next = newnode;newnode->_prev = pre;newnode->_next = pHead;pHead->_prev = newnode;}
// 雙向鏈表尾刪
void ListPopBack(ListNode* pHead) {assert(pHead);if (!ListEmptyLTNode(pHead)) {ListNode* tmp = pHead->_prev;ListNode* pre = pHead->_prev->_prev;pre->_next = pHead;pHead->_prev = pre;free(tmp);}}
// 雙向鏈表頭插
void ListPushFront(ListNode* pHead, LTDataType x) {assert(pHead);ListNode* newnode = BuySListNode(x);ListNode* ne = pHead->_next;newnode->_next = ne;ne->_prev = newnode;newnode->_prev = pHead;pHead->_next = newnode;
}
// 雙向鏈表頭刪
void ListPopFront(ListNode* pHead) {assert(pHead);ListNode* check = pHead->_next->_next;ListNode* tmp = pHead->_next;pHead->_next = check;check->_prev = pHead;free(tmp);
}
// 雙向鏈表查找
ListNode* ListFind(ListNode* pHead, LTDataType x) {assert(pHead);ListNode* start = pHead->_next;while (start != pHead) {if (start->_data == x)return start;start = start->_next;}return NULL;
}
// 雙向鏈表在pos的前面進行插入
void ListInsert(ListNode* pos, LTDataType x) {assert(pos);ListNode* newnode = BuySListNode(x);ListNode* pre = pos->_prev;newnode->_next = pos;pos->_prev = newnode;pre->_next = newnode;newnode->_prev = pre;
}
// 雙向鏈表刪除pos位置的節點
void ListErase(ListNode* pos) {assert(pos);ListNode* ne = pos->_next;ListNode* pre = pos->_prev;ne->_prev = pre;pre->_next = ne;free(pos);
}
test.c
#include "dlist.h"int main() {ListNode* head = ListCreate(); // 創建鏈表頭節點// 尾插入測試ListPushBack(head, 1);ListPushBack(head, 2);ListPushBack(head, 3);printf("鏈表內容(尾插入后):");ListPrint(head);// 頭插入測試ListPushFront(head, 0);printf("鏈表內容(頭插入后):");ListPrint(head);// 查找測試ListNode* found = ListFind(head, 2);if (found) {printf("找到節點:%d\n", found->_data);}else {printf("未找到節點\n");}// 刪除頭節點測試ListPopFront(head);printf("鏈表內容(頭刪后):");ListPrint(head);// 刪除尾節點測試ListPopBack(head);printf("鏈表內容(尾刪后):");ListPrint(head);// 在指定位置插入測試ListInsert(head->_next, 4); // 在第二個節點后插入printf("鏈表內容(插入4后):");ListPrint(head);// 刪除指定節點測試ListErase(head->_next); // 刪除第二個節點printf("鏈表內容(刪除第二個節點后):");ListPrint(head);// 銷毀鏈表ListDestory(head);// printf("鏈表內容(銷毀后):");// ListPrint(head); // 應該輸出空鏈表的狀態
head=NULL;return 0;
}
總結:

? ??? ?本篇關于雙鏈表的講解到這里就結束啦,后續小編會帶來更多精彩實用的內容,對你有幫助的可以點個贊,歡迎各位隊列交流學習

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

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

相關文章

Ubuntu如何查看硬盤的使用情況,以及掛載情況。

在Ubuntu中查看硬盤使用情況及掛載情況&#xff0c;可通過以下命令實現&#xff1a; 一、查看硬盤使用情況 df -h 顯示所有掛載文件系統的磁盤空間使用情況&#xff08;含總容量、已用空間、可用空間等&#xff09;&#xff0c;輸出結果以易讀格式&#xff08;如GB、MB&#x…

Github 2025-05-02Java開源項目日報 Top9

根據Github Trendings的統計,今日(2025-05-02統計)共有9個項目上榜。根據開發語言中項目的數量,匯總情況如下: 開發語言項目數量Java項目9Android開源輕量級流媒體前端 創建周期:3158 天開發語言:Java協議類型:GNU General Public License v3.0Star數量:28641 個Fork數量…

linux學習——數據庫API創建

一.API操作 1.int sqlite3_open(char *filename,sqlite3 **db) 功能&#xff1a;打開sqlite數據庫 參數&#xff1a; filename:數據庫文件路徑 db:指向sqlite句柄的指針 &#xff08;splite3* db;&#xff09; 返回值…

Baklib內容中臺落地實戰指南

內容中臺實施最佳路徑 在構建企業級內容中臺的實踐中&#xff0c;架構設計與流程優化構成核心支撐框架。通過四庫體系&#xff08;知識庫、資源庫、模板庫、場景庫&#xff09;的有機組合&#xff0c;企業可實現從知識沉淀到場景化應用的閉環管理。智能檢索技術結合語義分析引…

【重走C++學習之路】26、類型轉換

目錄 一、C語言中的類型轉換 二、C中的四個類型轉換 2.1 static_cast 2.2 dynamic_cast 2.3 const_cast 2.4 reinterpret_cast 2.5 總結 結語 一、C語言中的類型轉換 在C語言中&#xff0c;如果賦值運算符左右兩側類型不同&#xff0c;或者形參與實參類型不匹配&a…

kotlin 過濾 filter 函數的作用和使用場景

1. filter 函數的作用 filter 是 Kotlin 集合操作中的一個高階函數&#xff0c;用于根據指定條件從集合中篩選出符合條件的元素。 作用&#xff1a;遍歷集合中的每個元素&#xff0c;并通過給定的 lambda 表達式判斷是否保留該元素。返回值&#xff1a;一個新的集合&#xff…

安卓程序打包與發布

一 配置編譯信息 二 創建密鑰

LeetCode算法題 (移除鏈表元素)Day15!!!C/C++

https://leetcode.cn/problems/remove-linked-list-elements/description/ 一、題目分析 給你一個鏈表的頭節點 head 和一個整數 val &#xff0c;請你刪除鏈表中所有滿足 Node.val val 的節點&#xff0c;并返回 新的頭節點 。 今天的題目非常好理解&#xff0c;也就是要刪除…

Scrapy框架之【Scrapy-Redis】分布式爬蟲詳解

Scrapy-Redis 介紹 Scrapy-Redis 是一個基于 Redis 實現的 Scrapy 分布式爬蟲組件。Scrapy 本身是一個強大的 Python爬蟲框架&#xff0c;但它默認是單進程單線程的&#xff0c;在面對大規模數據抓取任務時效率不高。Scrapy-Redis 則解決了這一問題&#xff0c;它允許你將 Scra…

Gradio全解20——Streaming:流式傳輸的多媒體應用(3)——實時語音識別技術

Gradio全解20——Streaming&#xff1a;流式傳輸的多媒體應用&#xff08;3&#xff09;——實時語音識別技術 本篇摘要20. Streaming&#xff1a;流式傳輸的多媒體應用20.3 實時語音識別技術20.3.1 環境準備和開發步驟1. 環境準備2. ASR應用開發步驟&#xff08;基于Transform…

使用xlwings將兩張順序錯亂的表格進行數據核對

有如下一個excel表&#xff0c;姓名列的內容相同&#xff0c;順序不同&#xff1b;月薪有部分內容不同。 目的&#xff1a;要找出哪幾條月薪不同。 通常的做法&#xff0c;要使用excel的高級篩選。 在此&#xff0c;使用xlwings實現&#xff0c;在不同的內容上涂色。 代碼如…

2025大模型安全研究十大框架合集(10份)

2025大模型安全研究十大框架合集的詳細介紹&#xff1a; Anthropic AI信任研究框架 Anthropic于2024年10月更新的《安全責任擴展政策》(RSP)&#xff0c;提出了一個靈活的動態AI風險治理框架。該框架規定當AI模型達到特定能力時&#xff0c;將自動升級安全措施&#xff0c;如…

Qt/C++開發監控GB28181系統/云臺控制/獲取預置位信息/添加刪除調用預置位

一、前言 之前用onvif已經完美實現了設備的云臺控制和預置位的功能&#xff0c;這個基礎功能在監控系統中是使用頻率很高的&#xff0c;所有gb28181肯定也提供了這樣的功能&#xff0c;很多人以為是通過包含xml數據&#xff0c;對應節點指定對應的動作來實現&#xff0c;其實不…

第T8周:貓狗識別

● 語言環境&#xff1a;Python3.8.8 ● 編譯器&#xff1a;Jupyter Lab ● 深度學習環境&#xff1a;TensorFlow2.4.1 貓狗識別 一、前期工作1. 設置GPU 二、數據預處理1. 加載數據2.再次檢查數據3.配置數據集 三、構建VG-16網絡四、編譯五、訓練模型六、模型評估七、預測八、…

主流微前端框架比較

主流微前端框架比較 以下表格列出了當前主流微前端框架的核心對比信息,包括基本介紹、核心特性、適用場景、技術棧兼容性、優缺點、社區維護情況和典型應用案例等: 框架基本介紹核心特性與機制適用場景技術棧兼容性優缺點社區維護情況典型應用案例qiankun螞蟻金服推出的生產…

大學生入學審核系統設計與實現【基于SpringBoot + Vue 前后端分離技術】

一、項目概述 1.1 項目背景 隨著高校的不斷擴招&#xff0c;傳統的入學審核管理模式已不能滿足大規模學生數據的處理需求。人工管理不僅效率低下&#xff0c;還容易出現疏漏。本系統通過信息化手段&#xff0c;提升入學審核過程中的數據管理和審批效率。 1.2 系統目標 系統…

云計算-容器云-服務網格Bookinfo

服務網格&#xff1a;創建 Ingress Gateway 將 Bookinfo 應用部署到 default 命名空間下&#xff0c;請為 Bookinfo 應用創建一個網 關&#xff0c;使外部可以訪問 Bookinfo 應用。 上傳ServiceMesh.tar.gz包 [rootk8s-master-node1 ~]# tar -zxvf ServiceMesh.tar.gz [rootk…

Spring 分批處理 + 冷熱數據分離:歷史訂單高效遷移與數據清理實戰

在實際業務中&#xff0c;隨著時間推移&#xff0c;訂單量持續增長&#xff0c;若未及時進行數據治理&#xff0c;會造成數據庫膨脹、查詢緩慢、性能下降等問題。為了實現數據分層管理和系統高性能運行&#xff0c;我們在項目中采用了“冷熱數據分離 分批遷移 數據清理”的綜…

新手SEO優化核心步驟

內容概要 對于SEO新手而言&#xff0c;建立系統化的優化框架是突破入門瓶頸的關鍵。SEO的核心在于通過技術手段與內容策略的結合&#xff0c;提升網站在搜索引擎中的可見性與用戶價值。具體而言&#xff0c;新手需優先掌握關鍵詞研究&#xff0c;明確目標用戶的搜索意圖&#…

C++ 之 【list的簡介、list 的構造函數、iterator、容量操作、元素訪問、增刪查改與迭代器失效】

目錄 1.list的介紹 2.list的使用 2.1 構造函數 2.2 iterator 的使用 2.3 容量操作 2.4 元素訪問 2.5 增刪查改 2.5.1頭插頭刪與尾插尾刪 2.5.2 insert 、erase 函數 2.5.3 clear、swap函數 2.5.4 關于find函數 3.迭代器失效 1.list的介紹 (1)list的底層通常實現為帶…