C語言數據結構-雙向鏈表

文章目錄

  • 1 雙向鏈表的結構
  • 2 雙向鏈表的實現
    • 2.1 定義雙向鏈表的數據結構
    • 2.2 打印鏈表
    • 2.3 初始化鏈表
    • 2.4 銷毀鏈表
    • 2.5 尾插,頭插
    • 2.6 尾刪,頭刪
    • 2.7 根據頭次出現數據找下標
    • 2.8 定點前插入
    • 2.9 刪除pos位置
    • 2.10 定點后插入
  • 3 完整代碼
    • 3.1 List.h
    • 3.2 Lish.c
    • 3.3 test.c


1 雙向鏈表的結構

在這里插入圖片描述

帶頭鏈表的頭結點,實際是"哨兵位",哨兵位節點不存儲任何有效元素,只是站在這里"放哨的".
哨兵位的意義:遍歷循環鏈表避免死循環.

2 雙向鏈表的實現

筆者在刪除,插入數據時,畫好圖后,也好了代碼,但是在調試中多次出現代碼位置出錯,導致寫的代碼的含義不符合預期.
所以說思路一定要清晰,多多檢查調試

2.1 定義雙向鏈表的數據結構

typedef int ListDataType;typedef struct ListNode
{ListDataType data;		//整型數據struct ListNode* next;	//前驅指針struct ListNode* pre;	//后驅指針}ListNode;

2.2 打印鏈表

鏈表的第一個數據是phead->next,哨兵位不存儲數據
循環鏈表中,遍歷一遍,碰到phead為止

void ListPrint(ListNode* phead)
{ListNode* cur = phead->next;	//頭結點,哨兵位的下一位while (cur != phead)//雙向循環鏈表,循環到哨兵位為止{printf("%d-> ", cur->data);cur = cur->next;}
}

在這里插入圖片描述

2.3 初始化鏈表

定義一個雙向循環鏈表后,初始化鏈表,此時只有一個phead(哨兵位),前驅指針和后驅指針都指向phead自己
哨兵位的數據(data)在應用中不使用,就設置成-1了,與筆者之后使用的正整數形成差異

ListNode* ListInit()
{ListNode* phead = (ListNode*)malloc(sizeof(ListNode));if (phead == NULL){perror("malloc error");return;}phead->data = -1;phead->next = phead->pre = phead;return phead;
}

在這里插入圖片描述

調試中發現,phead,phead->next,phead->pre地址相同,data都是筆者設置的-1.

2.4 銷毀鏈表

遍歷一遍鏈表進行銷毀,cur碰到phead哨兵位為止
釋放cur前,記錄下cur->next,釋放cur后,把cur->next賦值給cur,以此避免銷毀cur后,cur->next不能指向下一個節點的情況
最后再把哨兵位釋放,置空.

void ListDestory(ListNode* phead)
{assert(phead);ListNode* cur = phead->next;	//頭結點,哨兵位的下一位while (cur!=phead){//釋放cur前,記錄下cur->next,釋放cur后,把cur->next賦值給curListNode* NEXT = cur->next;free(cur);cur = NEXT;}//釋放哨兵位free(phead);phead = NULL;
}

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

2.5 尾插,頭插

先寫一個創建內存空間的函數,創建node后,畫圖示意頭插和尾插
一定注意編寫代碼的順序,看看筆者注釋所說的
在這里插入圖片描述
在這里插入圖片描述

//插入數據前創建內存空間
ListNode* ListBuyNode(ListDataType x)
{ListNode* node = (ListNode*)malloc(sizeof(ListNode));node->data = x;node->next = node->pre = NULL;return node;
}void ListPushBack(ListNode* phead, ListDataType x)
{assert(phead);ListNode* node = ListBuyNode(x);//先處理新節點前驅指針和后驅指針node->pre = phead->pre;node->next = phead;//再處理原鏈表最后一個節點和pheadphead->pre->next = node;phead->pre = node;
}void ListPushFront(ListNode* phead, ListDataType x)
{assert(phead);ListNode* node = ListBuyNode(x);//先處理新節點前驅指針和后驅指針node->pre = phead;node->next = phead->next;//再處理phead和原鏈表第一個節點(phead->next)phead->next->pre = node;phead->next = node;}

初始化成功,我們插入一個數據"1",成功插入
在這里插入圖片描述
在這里插入圖片描述

2.6 尾刪,頭刪

刪除鏈表至少有除哨兵位的一個數據,換句話說,鏈表不能只有一個哨兵位
在這里插入圖片描述

在這里插入圖片描述

void ListPopBack(ListNode* phead)
{assert(phead);//鏈表不能只有一個哨兵位assert(phead->next != phead);ListNode* del = phead->pre;//刪除節點的前驅指針del->pre->next = phead;//phead的前驅指針phead->pre = del->pre;
}void ListPopFront(ListNode* phead)
{assert(phead && phead->next != phead);ListNode* del = phead->next;del->next->pre = phead;phead->next = del->next;free(del);del = NULL;}

在這里插入圖片描述

在這里插入圖片描述

2.7 根據頭次出現數據找下標

這個Find()函數在筆者的多篇博客都有提到缺點,但是我們主要實現功能,筆者在力扣題寫過找多個相同元素,刪多個相同元素的題

ListNode* ListFind(ListNode* phead, ListDataType x)
{assert(phead);ListNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}

在這里插入圖片描述

在這里插入圖片描述

2.8 定點前插入

在這里插入圖片描述

void ListInsert(ListNode* pos, ListDataType x)
{assert(pos);ListNode* node= ListBuyNode(x);//先處理nodenode->next = pos;node->pre = pos->pre;//在處理pos->pre和pospos->pre->next= node;node->next->pre = node;}

在這里插入圖片描述

2.9 刪除pos位置

在這里插入圖片描述

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

在這里插入圖片描述
在這里插入圖片描述

2.10 定點后插入

在這里插入圖片描述

void ListInsertAfter(ListNode* pos, ListDataType x)
{assert(pos);ListNode* node = ListBuyNode(x);//node的prev 和 nextnode->next = pos->next;node->pre = pos;//pos的next 和 pos->next的prevpos->next = node;node->next->pre = node;
}

在這里插入圖片描述

3 完整代碼

3.1 List.h

#define _CRT_SECURE_NO_WARNINGS
#include<stdbool.h>
#include<stddef.h>
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//0 定義雙向循環鏈表節點結構
typedef int ListDataType;typedef struct ListNode
{ListDataType data;struct ListNode* next;	//前驅指針struct ListNode* pre;	//后驅指針}ListNode;//0 打印鏈表
void ListPrint(ListNode* phead);//1 初始化鏈表
ListNode* ListInit();//2 銷毀鏈表
void ListDestory(ListNode* phead);//3 尾插,頭插
void ListPushBack(ListNode* phead, ListDataType x);
void ListPushFront(ListNode* phead, ListDataType x);//4 尾刪,頭刪
void ListPopBack(ListNode* phead);
void ListPopFront(ListNode* phead);//5 根據數找到第一次出現的下標
ListNode* ListFind(ListNode* phead, ListDataType x);//6 定點前插入
void ListInsert(ListNode* pos, ListDataType x);//7 刪除pos位置
void ListErase(ListNode* pos);//8 定點后插入
void ListInsertAfter(ListNode* pos, ListDataType x);

3.2 Lish.c

#include "List.h"void ListPrint(ListNode* phead)
{ListNode* cur = phead->next;while (cur != phead)//雙向循環鏈表,循環到哨兵位為止{printf("%d-> ", cur->data);cur = cur->next;}
}ListNode* ListInit()
{ListNode* phead = (ListNode*)malloc(sizeof(ListNode));if (phead == NULL){perror("malloc error");return;}phead->data = -1;phead->next = phead->pre = phead;return phead;
}void ListDestory(ListNode* phead)
{assert(phead);ListNode* cur = phead->next;	//頭結點,哨兵位的下一位while (cur!=phead){//釋放cur前提前記錄下cur->next,釋放cur后,把cur->next賦值給curListNode* NEXT = cur->next;free(cur);cur = NEXT;}//釋放哨兵位free(phead);phead = NULL;
}//插入數據前創建內存空間
ListNode* ListBuyNode(ListDataType x)
{ListNode* node = (ListNode*)malloc(sizeof(ListNode));node->data = x;node->next = node->pre = NULL;return node;
}void ListPushBack(ListNode* phead, ListDataType x)
{assert(phead);ListNode* node = ListBuyNode(x);//先處理新節點前驅指針和后驅指針node->pre = phead->pre;node->next = phead;//再處理原鏈表最后一個節點和pheadphead->pre->next = node;phead->pre = node;
}void ListPushFront(ListNode* phead, ListDataType x)
{assert(phead);ListNode* node = ListBuyNode(x);//先處理新節點前驅指針和后驅指針node->pre = phead;node->next = phead->next;//再處理phead和原鏈表第一個節點(phead->next)phead->next->pre = node;phead->next = node;}void ListPopBack(ListNode* phead)
{assert(phead);//鏈表不能只有一個哨兵位assert(phead->next != phead);ListNode* del = phead->pre;//刪除節點的前驅指針del->pre->next = phead;//phead的前驅指針phead->pre = del->pre;
}void ListPopFront(ListNode* phead)
{assert(phead && phead->next != phead);ListNode* del = phead->next;del->next->pre = phead;phead->next = del->next;free(del);del = NULL;}ListNode* ListFind(ListNode* phead, ListDataType x)
{assert(phead);ListNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}void ListInsert(ListNode* pos, ListDataType x)
{assert(pos);ListNode* node= ListBuyNode(x);//先處理nodenode->next = pos;node->pre = pos->pre;//在處理pos->pre和pospos->pre->next= node;node->next->pre = node;}void ListErase(ListNode* pos)
{assert(pos);pos->next->pre = pos->pre;pos->pre->next = pos->next;free(pos);pos = NULL;
}void ListInsertAfter(ListNode* pos, ListDataType x)
{assert(pos);ListNode* node = ListBuyNode(x);//node的prev 和 nextnode->next = pos->next;node->pre = pos;//pos的next 和 pos->next的prevpos->next = node;node->next->pre = node;
}

3.3 test.c

#include"List.h"void test1()
{ListNode* plist = ListInit();
}void test2()
{ListNode* plist = ListInit();;ListPushBack(plist, 1);ListPushBack(plist, 4);ListPushBack(plist, 7);ListPrint(plist);  //1->4->7->ListDestory(plist);
}void test3()
{ListNode* plist = ListInit();;ListPushFront(plist, 1);ListPushFront(plist, 4);ListPushFront(plist, 7);ListPrint(plist);//7->4->1->ListDestory(plist);
}void test4()
{ListNode* plist = ListInit();;ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPushBack(plist, 5);ListPushBack(plist, 6);ListPushBack(plist, 7);ListPushBack(plist, 8);ListPushBack(plist, 9);ListPrint(plist);printf("\n");ListPopBack(plist);ListPopBack(plist);ListPopFront(plist);ListPopFront(plist);ListPrint(plist);ListDestory(plist);
}void test5()
{ListNode* plist = ListInit();;ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPushBack(plist, 5);ListPrint(plist);printf("\n");//測試指定位置ListNode* Find1 = ListFind(plist, 2);ListNode* Find2 = ListFind(plist, 4);ListInsert(Find1, 10);ListInsertAfter(Find2, 20);ListPrint(plist);printf("\n");ListErase(Find1);ListPrint(plist);ListDestory(plist);
}int main()
{//test1();//test2();//test3();//test4();test5();return 0;
}

筆者在刪除,插入數據時,畫好圖后,也好了代碼,但是在調試中多次出現代碼位置出錯,導致寫的代碼的含義不符合預期.
所以說思路一定要清晰,多多檢查調試

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

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

相關文章

ajax中get和post的區別,datatype返回的數據類型有哪些?web開發中數據提交的幾種方式,有什么區別。百度使用哪種方式?

在Ajax中&#xff0c;GET和POST是兩種常見的HTTP請求方法。它們有以下區別&#xff1a; GET請求&#xff1a;使用GET請求時&#xff0c;參數數據會附加在URL的末尾&#xff0c;以查詢字符串的形式發送給服務器。GET請求是冪等的&#xff0c;也就是說多次發送相同的GET請求&…

鍵盤打字盲打練習系列之矯正坐姿——4

一.歡迎來到我的酒館 盲打&#xff0c;矯正坐姿&#xff01; 目錄 一.歡迎來到我的酒館二.繼續練習二.矯正坐姿1.鍵鼠快速選購指南2.椅子快速選購指南 三.改善坐姿建議 二.繼續練習 前面的章節&#xff0c;我們重點向大家介紹了主鍵盤區指法和鍵盤鍵位。經過一個系列的教程學習…

Mybatis環境搭建

1、開發環境 IDE&#xff1a;IntelliJ IDEA 2022.2.1 (Ultimate Edition) 構建工具&#xff1a;maven 3.6.1 MySQL版本&#xff1a;MySQL 5.7 MyBatis版本&#xff1a;MyBatis 3.5.14 2、工程創建 創建一個Maven工程giser-java-mybatis-demo 基礎依賴如下&#xff1a; &…

【Python】pip命令及使用

PIP命令 下面是一個整理成表格的pip命令及使用的示例&#xff1a; 命令使用示例說明pip install <package>pip install requests安裝名為"requests"的包pip uninstall <package>pip uninstall requests卸載名為"requests"的包pip listpip li…

用友U8 Cloud 多處反序列化RCE漏洞復現

0x01 產品簡介 用友U8 Cloud是用友推出的新一代云ERP,主要聚焦成長型、創新型企業,提供企業級云ERP整體解決方案。 0x02 漏洞概述 用友U8 Cloud存在多處(TableInputOperServlet、LoginServlet 、FileTransportServlet、CacheInvokeServlet、ActionHandlerServlet、Servle…

12.9每日一題(備戰藍橋杯循環結構)

12.9每日一題&#xff08;備戰藍橋杯循環結構&#xff09; 題目 2165: 求平均年齡題目描述輸入輸出樣例輸入樣例輸出來源/分類 題解 2165: 求平均年齡題目 2166: 均值題目描述輸入輸出樣例輸入樣例輸出來源/分類 題解 2166: 均值題目 2167: 求整數的和與均值題目描述輸入輸出樣…

GB/T 43212-2023 竹炭板檢測

竹炭塑復合板是指以竹炭粉為主要原料&#xff0c;與塑料及其他助劑復配混合&#xff0c;經熔融擠出或模壓成型等工藝制成的板材。 GB/T 43212-2023 竹炭板測試&#xff1a; 測試項目 測試方法 外觀 GB/T 43212 尺寸 GB/T 19367 含水率 GB/T 17657 密度 GB/T 17657 吸…

【rabbitMQ】springboot整合rabbitMQ模擬簡單收發消息

目錄 1.創建項目和模塊 2.添加rabbitMQ依賴 3.啟動rabbitMQ服務 4.引入rabbitMQ服務端信息 5.通過單元測試模擬業務發送消息 6. 接收消息 1.創建項目和模塊 2.添加rabbitMQ依賴 <!-- rabbitmq依賴--> <dependency> <groupId>org.sp…

JavaEE 09 鎖策略

1.鎖策略 1.1 樂觀鎖與悲觀鎖 其實前三個鎖是同一種鎖,只是站在不同的角度上去進行描述,此處的樂觀與悲觀其實是指在預測的角度上看會發生鎖競爭的概率大小,概率大的則是悲觀鎖,概率小的則是樂觀鎖 樂觀鎖在加鎖的時候就會做較少的事情,加鎖的速度較快,但是消耗的cpu資源等也會…

排序算法-選擇/堆排序(C語言)

1基本思想&#xff1a; 每一次從待排序的數據元素中選出最小&#xff08;或最大&#xff09;的一個元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的 數據元素排完 。 2 直接選擇排序: 在元素集合 array[i]--array[n-1] 中選擇關鍵碼最大 ( 小 ) 的數據元素…

PHP基礎 - 數組遍歷與排序

介紹 在PHP中,數組遍歷和排序是常見的操作,用于對數組中的元素進行訪問和排序 數組遍歷 1)數值數組的遍歷 使用 foreach 循環遍歷數組:foreach 循環是最常用的遍歷數組的方法,它可以遍歷索引數組和關聯數組。例如:$fruits = array("apple", "banana&q…

AG1KLPQ48 User Manual

1.&#xff09;軟件安裝&#xff1a; 解壓縮或執行安裝文件&#xff0c;安裝 Supra 軟件。執行文件為 bin 目錄中的 Supra.exe。 運行 Supra&#xff0c;選擇菜單 File -> Import license&#xff0c;選擇 license 文件并導入 License。 2.&#xff09;新建項目&#xff1a;…

Python與CAD系列高級篇(二十一)批量將橫向文本改豎向

0 簡述 本篇介紹以下功能開發:一次性選擇所有橫向文本并批量修改為豎向。 1 需求 需求: ① 用戶在cad中交互式選擇所有需要修改方向的文本。 ② 將所有文本方向由橫向改為豎向。 2 代碼實現 代碼實現: import win32com.client as win32 import pythoncomdef vtpnt(x, y, …

Elaticsearch 學習筆記

文章目錄 Elaticsearch 學習筆記一、什么是 Elaticsearch &#xff1f;二、Elaticsearch 安裝1 es 安裝2 問題解決3 數據格式 三、索引操作1 PUT 請求&#xff1a;在postman中&#xff0c;向 ES 服務器發 PUT 請求&#xff08;PUT請求相當于創建的意思&#xff09;2 GET 請求&a…

Base64編碼解碼

一、Base64編碼技術簡介 Base64編碼是一種廣泛應用于網絡傳輸和數據存儲的編碼方式。它將原始數據轉換為可打印的字符形式&#xff0c;以便于傳輸和存儲。Base64編碼后的數據長度是原始數據長度的約3/4&#xff0c;具有一定的壓縮效果。 Base64編碼解碼 -- 一個覆蓋廣泛主題工…

【trino權威指南】使用trino詳解:trino client安裝、查詢sql、DBeaver連接trino、java通過JDBC連接trino

文章目錄 一. Trino CLI1. 安裝client2. 使用client執行sql 二. JDBC driver 連接Trino1. 通過DBeaver用戶界面連接2. JDBC Driver in java2.1. 環境配置2.2. 注冊和配置driver2.3. 連接參數2.4. 查詢例子 一. Trino CLI 1. 安裝client Trino CLI提供了一個基于終端的交互式s…

上海交通大學生存手冊PDF

強烈推薦所有大學生去閱讀《上海交通大學生存手冊》。雖然它可能有些冗長&#xff0c;但非常重要&#xff0c;因為它道出了大學教育的本質。 如果幾年前我能夠看到這本書&#xff0c;也許我的大學生活會有所不同。現在我將向正在上大學或者將要上大學的你推薦這本書。 無論你…

通過虛擬機安裝Open5GS 和UERANSIM記錄

目錄 wsl虛擬環境嘗試失敗 step1 安裝wsl: step2下載Ubuntu 20.04.6 LTS: step3升級wsl&#xff1a; step4生成用戶: step5 linux下安裝軟件需要的鏡像&#xff1a; step6 安裝圖形界面xfce和瀏覽器&#xff1a; step6 安裝chrome virtual box安裝ubuntu step7&#xf…

AWS攻略——Peering連接VPC

文章目錄 創建IP/CIDR不覆蓋的VPC創建VPC創建子網創建密鑰對創建EC2 創建Peering接受Peering邀請修改各個VPC的路由表修改美東us-east-1 pulic subnet的路由修改悉尼ap-southeast-2路由 測試知識點 我們回顧下《AWS攻略——VPC初識》中的知識&#xff1a; 一個VPC只能設置在一…

Android引用SDK包實現高德地圖展示

一、準備工作 注冊高德地圖開放平臺 注冊過程我就不多說了&#xff0c;挺簡單的&#xff0c;需要登錄&#xff0c;然后注冊成為開發者&#xff0c;還需要支付寶認證、手機號碼驗證、郵箱驗證挺多的&#xff0c;但是速度很快。基本上隨時驗證隨時注冊成功。新建應用新建…