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

前言:

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


目錄

前言

? ? ? ? ? ?概念:

單鏈表的結構

我們設定一個哨兵位頭節點給鏈表

單鏈表的基本操作

插入:

單鏈表的頭插:

單鏈表的尾插:

單鏈表的遍歷打印:從頭節點開始,逐個訪問鏈表中的每個節點,直到達到鏈表的末尾。

單鏈表的刪除:刪除鏈表中的指定節點,需要修改前一個節點的指針域以繞過被刪除的節點。

單鏈表的頭刪:

單鏈表的尾刪:

單鏈表元素的查找:根據給定的值或位置查找鏈表中的節點。

單鏈表的鏈表銷毀:

驗證:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

完整代碼:

slist.h

slist.c

test.c

總結:


概念:

? ? ? ?單鏈表是一種鏈式存取的數據結構,用一組地址任意的存儲單元存放線性表中的數據元素。鏈表中的數據是以結點來表示的,每個結點的構成:元素 (數據元素的映象) + 指針 (指示后繼元素存儲位置),元素就是存儲數據的存儲單元,指針就是連接每個結點的地址數據。單鏈表不要求邏輯上相鄰的兩個元素在物理位置上也相鄰,因此不需要連續的存儲空間。單鏈表是非隨機的存儲結構,即不能直接找到表中某個特定的結點。查找某個特定的結點時,需要從表頭開始遍歷,依次查找。?

單鏈表的結構

? ? ? ?在單鏈表中,每個節點由一個數據元素和一個指針構成。數據元素可以是任何類型,而指針則指向鏈表中的下一個節點。如果一個節點的指針為空(NULL),則表示它是鏈表的最后一個節點。單鏈表通常通過一個頭指針來訪問,頭指針指向鏈表的第一個節點。

一個典型的單鏈表節點的結構可以用以下C語言代碼表示:

typedef struct SListNode
{SLTDateType data;//數據域struct SListNode* next;// 指針域,指向下一個節點
}SListNode;
我們設定一個哨兵位頭節點給鏈表
 SListNode* head = NULL; 
單鏈表的基本操作

單鏈表的基本操作包括初始化、遍歷、插入、刪除和查找等。

單鏈表的初始化:創建一個空鏈表,通常是通過創建一個頭節點,其指針域為空。

插入:

在鏈表的指定位置插入一個新節點,需要修改前一個節點的指針域。

單鏈表的頭插:

? ? ? ? 斷言確保頭指針地址有效,先去動態申請一個新節點,然后將新節點的下一個節點指向頭節點,將該新節點變為頭節點,(不需要判斷是否為空)


// 單鏈表的頭插
void SListPushFront(SListNode** pplist, SLTDateType x) {assert(pplist);SListNode* newnode = BuySListNode(x);//SListNode* start = *pplist;newnode->next = *pplist;*pplist = newnode;}
單鏈表的尾插:

? ? ? 斷言確保頭指針地址有效,再申請一個新節點,分兩種情況,當鏈表為空時,直接新節點就是頭節點,鏈表不為空時,從頭節點遍歷到最后一個節點,將新節點設為最后一個節點的下一個節點

// 單鏈表尾插
void SListPushBack(SListNode** pplist, SLTDateType x) {assert(pplist);SListNode* newnode = BuySListNode(x);SListNode* end = *pplist;if (*pplist != NULL) //鏈表非空{while (end->next != NULL)end = end->next;end->next = newnode;}else //鏈表為空{*pplist = newnode;}
}

? ? ?動態創建一個SListNode大小的節點,然后看看是否創建成功,成功后將數據放入該節點,并將該節點的下一個位置,置為空;


// 動態申請一個節點
SListNode* BuySListNode(SLTDateType x) {SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL) {perror("malloc fail");return NULL;}// 初始化數據域和指針域newnode->data = x;newnode->next = NULL;return newnode;
}
單鏈表在pos位置前插入
// 在pos的前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x) {assert(pphead);assert(*pphead);assert(pos);if (*pphead == pos)SListPushFront(pphead, x);else {SListNode* ne = pos;SListNode* newnode = BuySListNode(x);SListNode* start = *pphead;while (start->next != pos) {start = start->next;if (start == NULL) {assert(0);return;}}start->next = newnode;newnode->next = ne;}}
單鏈表在pos位置后插入
// 單鏈表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x) {assert(pos);/*SListNode* pre = pos;*/SListNode* newnode = BuySListNode(x);SListNode* ne = pos->next;pos->next = newnode;newnode->next = ne;}
單鏈表的遍歷打印從頭節點開始,逐個訪問鏈表中的每個節點,直到達到鏈表的末尾。

? 創建一個頭指針,遍歷一遍鏈表,每遍歷一個節點,將這個節點的數據打印出來;

// 單鏈表打印
void SListPrint(SListNode* plist) {SListNode* head = plist;while (head != NULL) // 遍歷鏈表直到NULL{printf("%d->", head->data);head = head->next;}printf("NULL\n");}

單鏈表的刪除:刪除鏈表中的指定節點,需要修改前一個節點的指針域以繞過被刪除的節點。

單鏈表的頭刪:

? ? ? 斷言確保頭指針地址有效,以及頭指針不為空,將頭指針保存好,然后將頭指針置為頭指針的下一個位置,再將保存的指針釋放,并置為空

// 單鏈表頭刪
void SListPopFront(SListNode** pplist) {assert(pplist);assert(*pplist);SListNode* start = *pplist;*pplist = (*pplist)->next;free(start);start = NULL;}
單鏈表的尾刪:

斷言確保頭指針地址有效,以及頭指針不為空,分兩種情況,

一種是頭指針下一個節點為空,直接將其釋放并置為空;

另外一種情況呢,就是設兩個指針,找尾和尾的前驅節點,然后將尾節點釋放并置為空,再將尾的前驅節點的下一個置為空

// 單鏈表的尾刪
void SListPopBack(SListNode** pplist) {assert(pplist);assert(*pplist);//暴力檢查是否為空鏈表,空鏈表不能刪數據SListNode* end = *pplist;if ((*pplist)->next== NULL) {free(*pplist);*pplist = NULL;}else{//找尾SListNode* prev = *pplist;SListNode* tail = *pplist;while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;//假如只有一個節點這里就會非法訪問}}
刪除pos位置
// 刪除pos位置
void SLTErase(SListNode** pphead, SListNode* pos) {assert(pphead);assert(*pphead);assert(pos);if (pos == *pphead)SListPopFront(pphead);else {SListNode* start = *pphead;while (start->next != pos) {start = start->next;if (start == NULL) {assert(0);return;}}start->next = pos ->next;free(pos);pos = NULL;}
}
單鏈表元素的查找:根據給定的值或位置查找鏈表中的節點。

? ? ? ? 創建一個節點start將頭節點指針復制一個副本,然后再指針不為空時不斷將其指向下一個的同時,看看該節點的值與所要查找的值是否相等,相等返回該節點的結構體指針,當遍歷完鏈表還沒有找到,返回NULL;

// 單鏈表查找
SListNode* SListFind(SListNode* plist, SLTDateType x) {SListNode* start = plist;while (start != NULL) {if (start->data == x)return start;start = start->next;}return NULL;
}

單鏈表的鏈表銷毀:

? ? ? ?先判斷頭指針是否為空,為空直接返回,不為空時創建一個start頭指針儲存頭節點,再通過start遍歷整個鏈表,在遍歷過程中,將每一個節點釋放掉,最后再將頭節點釋放,置為空;

//銷毀鏈表
void SLTDestroy(SListNode** pphead) {if (*pphead == NULL) {return;}else {SListNode* start = *pphead;while (start != NULL) {SListNode* ne = start->next;free(start);start = ne;}free(*pphead);*pphead = NULL;}
}
驗證:

我們再創建一個main的測試函數測試一下我們實現的單鏈表的各個功能有沒有問題:

完整代碼:
slist.h
#pragma once
// slist.h
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int SLTDateType;
typedef struct SListNode
{SLTDateType data;struct SListNode* next;
}SListNode;// 動態申請一個節點
SListNode* BuySListNode(SLTDateType x);
// 單鏈表打印
void SListPrint(SListNode* plist);
// 單鏈表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 單鏈表的頭插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 單鏈表的尾刪
void SListPopBack(SListNode** pplist);
// 單鏈表頭刪
void SListPopFront(SListNode** pplist);
// 單鏈表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 單鏈表在pos位置之后插入x
// 分析思考為什么不在pos位置之前插入
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 單鏈表刪除pos位置之后的值
// 分析思考為什么不刪除pos位置?
void SListEraseAfter(SListNode* pos);// 在pos的前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x);
// 刪除pos位置
void SLTErase(SListNode** pphead, SListNode* pos);
void SLTDestroy(SListNode** pphead);
slist.c
#include "slist.h"
// 動態申請一個節點
SListNode* BuySListNode(SLTDateType x) {SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL) {perror("malloc fail");return NULL;}newnode->data = x;newnode->next = NULL;return newnode;
}
// 單鏈表打印
void SListPrint(SListNode* plist) {SListNode* head = plist;while (head != NULL) {printf("%d->", head->data);head = head->next;}printf("NULL\n");}
// 單鏈表尾插
void SListPushBack(SListNode** pplist, SLTDateType x) {assert(pplist);SListNode* newnode = BuySListNode(x);SListNode* end = *pplist;if (*pplist != NULL) {while (end->next != NULL)end = end->next;end->next = newnode;}else {*pplist = newnode;}
}
// 單鏈表的頭插
void SListPushFront(SListNode** pplist, SLTDateType x) {assert(pplist);SListNode* newnode = BuySListNode(x);//SListNode* start = *pplist;newnode->next = *pplist;*pplist = newnode;}
// 單鏈表的尾刪
void SListPopBack(SListNode** pplist) {assert(pplist);assert(*pplist);//暴力檢查是否為空鏈表,空鏈表不能刪數據SListNode* end = *pplist;if ((*pplist)->next== NULL) {free(*pplist);*pplist = NULL;}else{//找尾SListNode* prev = *pplist;SListNode* tail = *pplist;while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;//假如只有一個節點這里就會非法訪問}}
// 單鏈表頭刪
void SListPopFront(SListNode** pplist) {assert(pplist);assert(*pplist);SListNode* start = *pplist;*pplist = (*pplist)->next;free(start);start = NULL;}
// 單鏈表查找
SListNode* SListFind(SListNode* plist, SLTDateType x) {SListNode* start = plist;while (start != NULL) {if (start->data == x)return start;start = start->next;}return NULL;
}
// 單鏈表在pos位置之后插入xvoid SListInsertAfter(SListNode* pos, SLTDateType x) {assert(pos);/*SListNode* pre = pos;*/SListNode* newnode = BuySListNode(x);SListNode* ne = pos->next;pos->next = newnode;newnode->next = ne;}
// 單鏈表刪除pos位置之后的值void SListEraseAfter(SListNode* pos) {assert(pos);assert(pos->next);SListNode* pre = pos;SListNode* ne = pos->next->next;free(pos->next);pos->next = ne;
}// 在pos的前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x) {assert(pphead);assert(*pphead);assert(pos);if (*pphead == pos)SListPushFront(pphead, x);else {SListNode* ne = pos;SListNode* newnode = BuySListNode(x);SListNode* start = *pphead;while (start->next != pos) {start = start->next;if (start == NULL) {assert(0);return;}}start->next = newnode;newnode->next = ne;}}
// 刪除pos位置
void SLTErase(SListNode** pphead, SListNode* pos) {assert(pphead);assert(*pphead);assert(pos);if (pos == *pphead)SListPopFront(pphead);else {SListNode* start = *pphead;while (start->next != pos) {start = start->next;if (start == NULL) {assert(0);return;}}start->next = pos ->next;free(pos);pos = NULL;}
}
void SLTDestroy(SListNode** pphead) {if (*pphead == NULL) {free(pphead); pphead = NULL;}else {SListNode* start = *pphead;while (start != NULL) {SListNode* ne = start->next;free(start);start = ne;}*pphead = NULL;}
}
test.c
#include "slist.h"int main() {SListNode* head = NULL; // 尾插SListPushBack(&head, 1);SListPushBack(&head, 2);SListPushBack(&head, 3);printf("鏈表內容(尾插入后):");SListPrint(head);// 頭插SListPushFront(&head, 0);printf("鏈表內容(頭插入后):");SListPrint(head);// 查找SListNode* found = SListFind(head, 2);if (found) {printf("找到節點:%d\n", found->data);}else {printf("未找到節點\n");}// 頭刪SListPopFront(&head);printf("鏈表內容(頭刪后):");SListPrint(head);// 尾刪SListPopBack(&head);printf("鏈表內容(尾刪后):");SListPrint(head);// pos插入SListInsertAfter(head, 4); printf("鏈表內容(插入4后):");SListPrint(head);// pos刪除SLTErase(&head, head->next);printf("鏈表內容(刪除第二個節點后):");SListPrint(head);// 銷毀SLTDestroy(&head);//destroy后不能再訪問// printf("鏈表內容(銷毀后):");//SListPrint(head); return 0;
}

總結:

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

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

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

相關文章

【JAVA】數據類型與變量:深入理解棧內存分配(4)

核心知識點詳細解釋 Java 的基本數據類型和引用數據類型 基本數據類型 Java 有 8 種基本數據類型&#xff0c;它們可以分為 4 類&#xff1a; 整數類型&#xff1a;byte&#xff08;1 字節&#xff09;、short&#xff08;2 字節&#xff09;、int&#xff08;4 字節&#…

ReentrantLock實現公平鎖和非公平鎖

在 Java 里&#xff0c;公平鎖和非公平鎖是多線程編程中用于同步的兩種鎖機制&#xff0c;它們的主要差異在于獲取鎖的順序規則。下面是對二者的詳細介紹&#xff1a; 公平鎖 公平鎖遵循 “先來先服務” 原則&#xff0c;也就是線程獲取鎖的順序和請求鎖的順序一致。先請求鎖…

一篇擼清 Http,SSE 與 WebSocket

HTTP,SSE 和WebSocket都是網絡傳輸的協議,本篇快速介紹三者的概念和比較。 SSE(Server-Sent Events) 是什么? SSE(Server-Sent Events),服務器發送事件, 是一種基于 HTTP 的輕量級協議,允許服務器主動向客戶端(如瀏覽器)推送實時數據。它設計用于單向通信(服務器到…

5個重要的財務指標講解

1&#xff09;凈資產收益率 2&#xff09;銷售凈利率 3&#xff09; 銷售毛利率 4&#xff09;銷售成本率 5&#xff09; 期間費用率 好的&#xff0c;我將通過一個假設的案例&#xff08;某公司2023年數據&#xff09;逐步解釋這些財務指標&#xff0c;并用具體數字演示計算…

PISI:眼圖1:眼圖相關基本概念

0 英文縮寫 TIE&#xff08;Time Interval Error&#xff09;時間間隔誤差&#xff0c;UI&#xff08;Unit Interval&#xff09;單位間隔PDF&#xff08;Probability Density Function&#xff09;概率密度函數BER&#xff08;Bit Error Rate&#xff09;誤碼率TJ&#xff08…

前端八股 CSS 2 選擇器

選擇器功能&#xff1a;選中特定 DOM節點進行渲染 原始方法 getElementById() getElementByName() 現在方法選擇器 分類&#xff1a; id選擇器 類選擇器 標簽選擇器 邏輯與選擇器 其他類型選擇器&#xff1a; 偽類選擇器&#xff1a; :link&#xff1a;未被訪問的鏈接…

算法競賽進階指南.闇の連鎖

目錄 題目算法標簽: 樹上差分, L C A LCA LCA, 倍增思路代碼 題目 352. 闇の連鎖 算法標簽: 樹上差分, L C A LCA LCA, 倍增 思路 對于一個無向圖, 第一次切斷樹邊, 第二次切非樹邊, 一共多少種方案使得圖不連通, 點數和邊數都很大, 時間復雜度不能是 O ( n 2 ) O(n ^ 2…

ActiveMQ 與其他 MQ 的對比分析:Kafka/RocketMQ 的選型參考(二)

ActiveMQ、Kafka 和 RocketMQ 詳細對比 性能對比 在性能方面&#xff0c;Kafka 和 RocketMQ 通常在高吞吐量場景下表現出色&#xff0c;而 ActiveMQ 則相對較弱。根據相關測試數據表明&#xff0c;Kafka 在處理大規模日志數據時&#xff0c;單機吞吐量可以達到每秒數十萬條甚…

Electron 從零開始:構建你的第一個桌面應用

&#x1f5a5;? Electron 從零開始&#xff1a;構建你的第一個桌面應用 Electron 是一個可以使用 HTML、CSS 和 JavaScript 構建跨平臺桌面應用的框架。它將 Chromium 和 Node.js 融合到一個環境中&#xff0c;使 Web 開發者也能輕松開發原生桌面應用。 &#x1f680; 什么是 …

相向雙指針-16. 最接近的三數之和

16. 最接近的三數之和 題目描述思路講解代碼展示復雜度分析相關標簽 題目描述 思路講解 思路和 15. 三數之和 類似&#xff0c;排序后&#xff0c;枚舉 nums[i] 作為第一個數&#xff0c;那么問題變成找到另外兩個數&#xff0c;使得這三個數的和與 target 最接近&#xff0c;…

C 語 言 - - - 文 件 操 作

C 語 言 - - - 文 件 操 作 文 件文 件 名文 件 操 作fopenfclose 文 件 的 順 序 讀 寫fputcfgetcfputsfgetsfprintffscanffwritefread 流文 件 的 隨 機 讀 寫fseekftellrewind 總結 &#x1f4bb;作 者 簡 介&#xff1a;曾 與 你 一 樣 迷 茫&#xff0c;現 以 經 驗 助 你…

Walrus 與 Pudgy Penguins 達成合作,為 Web3 頭部 IP 引入去中心化存儲

以將深受喜愛的數字藏品賦予生命而聞名的 IP 與品牌開發公司 Pudgy Penguins&#xff0c;現已集成 Walrus&#xff0c;用于存儲和管理其日益增長的數字媒體資源庫&#xff0c;包括在其產品和社區體驗中使用的貼紙和 GIF。團隊將率先通過 Tusky&#xff08;Walrus 的用戶友好型文…

2019ICPC陜西省賽暨陜西邀請賽題解 BCDEF HIJKL

共111支隊伍&#xff0c;獲獎情況&#xff08;大概&#xff09; 銅牌66 —— 3 296 銀牌33 —— 4 391 金牌 11 —— 6 808 題目難度&#xff08;過題&#xff09;L F E B C I J D K H Problem - L - Codeforces 思路&#xff1a;注意到答案是連乘&#xff0c;只要有0…

5塊錢的無憂套餐卡可以變成流量卡嗎

電信的 5 塊錢無憂套餐卡理論上可以變成流量卡&#xff0c;但會受到一些條件限制&#xff0c;以下是具體介紹&#xff1a; 中國電信無憂卡簡介 中國電信無憂卡是電信推出的低月租套餐&#xff0c;月租僅 5 元&#xff0c;包含 200M 國內流量、來電顯示和 189 郵箱&#xff0c;全…

SpringBoot校園失物招領平臺源碼開發實現

概述 實用的??SpringBoot校園失物招領平臺??完整項目源碼&#xff0c;幫助開發者快速構建校園失物招領系統。該項目采用SpringBootVue前后端分離架構&#xff0c;包含完整的注冊登錄、信息發布、認領管理等模塊&#xff0c;是學習企業級項目開發的優秀范例 主要內容 1. …

如何在純C中實現類、繼承和多態(小白友好版)

基本實現原理 /* 通過結構體函數指針模擬類 */ typedef struct {// 成員變量int x; // 成員方法&#xff08;函數指針&#xff09; void (*print)(void* self); } MyClass;/* 成員函數實現 */ void my_print(void* self) {MyClass* obj (MyClass*)self;p…

51單片機入門教程——每個音符對應的重裝載值

前言 本教程基于B站江協科技課程進行個人學習整理&#xff0c;專為擁有C語言基礎的零基礎入門51單片機新手設計。既幫助解決因時間差導致的設備迭代調試難題&#xff0c;也助力新手快速掌握51單片機核心知識&#xff0c;實現從C語言理論到單片機實踐應用的高效過渡 。

股票單因子的檢驗方法有哪些?

股票單因子的檢驗方法主要包括以下四類方法及相關指標&#xff1a; 一、統計指標檢驗 IC值分析法 定義&#xff1a;IC值&#xff08;信息系數&#xff09;衡量因子值與股票未來收益的相關性&#xff0c;包括兩種計算方式&#xff1a; Normal IC&#xff1a;基于Pearson相關系數…

洛谷 P8606 [藍橋杯 2013 國 B] 高僧斗法 博弈論

題目 傳送門 P8606 [藍橋杯 2013 國 B] 高僧斗法 - 洛谷 思路 這個題就比較考驗博弈的基本題型和轉換能力了&#xff1b; 這個題是nim博弈>階梯博弈 再將小和尚的移動轉化為階梯上石子的移動&#xff1a;兩個小和尚之間可以移動的距離&#xff0c;看做階梯上的石子&…

《政治最后的日子》章節

政治與中世紀教會的類比性衰落 作者提出現代民族國家正重復中世紀教會的衰落軌跡&#xff1a; 兩者均曾作為社會組織核心存在約5個世紀 晚期都成為生產力阻礙&#xff08;中世紀教會稅收負擔/現代國家官僚低效&#xff09; 末期均出現管理者普遍腐敗與公眾蔑視&#xff08;…