筆記六:單鏈表鏈表介紹與模擬實現

在他一生中,從來沒有人能夠像你們這樣,以他的視角看待這個世界。

?????????????????????????????????????????????????????????????????????????????????????????????????????????????---------《尋找天堂》? ??

目錄

文章目錄

一、什么是鏈表?

二、為什么要使用鏈表?

三、 單鏈表介紹與使用

? ? ? ? 3.1 單鏈表

? ? ? ? ?3.1.1 創建單鏈表節點

????????3.1.2?單鏈表的頭插、尾插

? ? ? ? ?單鏈表的尾插

? ? ? ? ?單鏈表的頭插

3.1.3? 單鏈表的頭刪、尾刪

?????????單鏈表的尾刪?編輯

?????????單鏈表的頭刪

?3.1.4?鏈表節點的查找

3.1.5?在指定位置插入數據

????????在指定位置前插入數據

?????????在指定位置之后插入數據

?3.1.6?刪除pos之后的節點

3.1.7?銷毀鏈表

?四、完整代碼

SList.h

SList.c


一、什么是鏈表?

????????鏈表是一種物理存儲上非連續,數據元素的邏輯順序通過鏈表中的指針鏈接次序,實現的一種線性存儲結構。鏈表由一系列節點(鏈表中每一個元素稱為節點)組成,節點在運行時動態生成 (malloc),每個節點包括兩個部分:

  • ? ? ?一個是存儲數據元素的數據域
  • ? ? ?另一個是存儲下一個節點地址的指針域

二、為什么要使用鏈表?

? ? ? ? ?使用鏈表用于解決動態數量的數據存儲。比如說,我們要管理一堆票據,可能有一張,但也可能有一億張。怎么辦呢?申請一個幾十G的大數組存儲著嗎?萬一用戶只有一百張票據要處理呢?內存較小拒絕運行?可申請少了又不夠用啊……

????????而且,用數組的話,刪除然后添加票據,是每次刪除讓后面五百萬張往前移一格呢、還是每次添加從頭搜索空閑格子?如何區分空閑格子和值為0的數據?要進行區分的話是多占用空間呢還是占用數據值域?占用了值域會不會使得數據處理變得格外復雜?會不會一不小心就和正常數據混淆?萬一拿來區分的字段在某個版本后廢棄不用、或者擴充值域了呢?

????????那么,鏈表這種東西就是個很有效的數據結構,可以很有效的管理這類不定量的數據。

三、 單鏈表介紹與使用

? ? ? ? 3.1 單鏈表

????????單鏈表的每個節點除了存放數據元素外,還要存儲指向下一個節點的指針。與順序表進行對比:

優點缺點
順序表可隨機存儲,存儲密度較高要求大片連續空間,改變容量不方便
單鏈表不要求大片連續空間,改變容量方便不可隨機存取,要耗費一定空間存放指針

? ? ? ? ?3.1.1 創建單鏈表節點

typedef int SLTDataType;
//鏈表是由節點構成
//邏輯結構:一定連續;物理結構:不一定連續
//不會造成空間的浪費,由獨立的節點構成
typedef struct SListNode {  //SList : single linked list 單鏈表SLTDataType data;  // ?一個是存儲數據元素的數據域struct SListNode* next;  //? ?另一個是存儲下一個節點地址的指針域
}SLTNode;

? ? ? ? 上面的結構體僅是單鏈表節點的定義,要創建一新的節點需要對里面的數據進行初始化:插入數值,節點指向的下一個節點為空

//創建一個節點
struct SListNode* SLTBuyNode(SLTDataType x) {SLTNode* newnode = (struct SListNode*)malloc(sizeof(struct SListNode));newnode->data = x;newnode->next = NULL;return newnode;
}

????????3.1.2?單鏈表的頭插、尾插

? ? ? ? ?單鏈表的尾插

? ? ? ? 這里傳遞的鏈表節點的參數,為什么是二級指針呢?

????????在初始化過程中,需要修改頭指針,因此要用到二級指針傳遞頭指針的地址,這樣才能修改頭指針。這與普通變量類似,當需要修改普通變量的值,需傳遞其地址。使用二級指針,很方便就修改了傳入的結點一級指針的值。 如果用一級指針,則只能通過指針修改指針所指內容,卻無法修改指針的值,也就是指針所指的內存塊。

//鏈表的尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x) { //一級指針傳遞的為結構體變量地址的值,二級指針接收的是指向結構體變量的指針的地址assert(pphead);SLTNode* newnode = SLTBuyNode(x);//鏈表為空,新節點作為ppheadif (*pphead == NULL) {*pphead = newnode;return;}//鏈表不為空,鏈表的尾指向新節點SLTNode* ptail = *pphead;while (ptail->next) {ptail = ptail->next;}ptail->next = newnode;
}
? ? ? ? ?單鏈表的頭插

void SLTPushFront(SLTNode** pphead, SLTDataType x) {assert(pphead);//鏈表為空,新節點指向pphead,pphead再指向新節點;鏈表不為空,新節點指向pphead,pphead再指向新節點SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;*pphead = newnode;
}

? ? ? ? ?插入完數據后,想要打印以一下鏈表的數據,通過從頭開始一個一個節點地訪問數據,并打印,便實現了鏈表數據的打印。然后看看插入的情況;

//打印鏈表
void SLTPrint(SLTNode* phead) {SLTNode* pcur = phead;while (pcur) {printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}

? ? ? ? ?尾插部分數據,觀察打印的結果是否符合預期結果;發現運行結果與預期相符,所以鏈表的插入操作是沒什么大問題的

3.1.3? 單鏈表的頭刪、尾刪

????????如果鏈表為空,不需要刪除;如果刪除的是第一個結點,則需要將保存鏈表首地址的指針保存第一個結點的下一個結點的 地址 如果刪除的是中間結點,則找到中間結點的前一個結點,讓前一個結點的指針域保存這個結 點的后一個結點的地址即可?

?????????單鏈表的尾刪
void SLTPopBack(SLTNode** pphead) {assert(pphead);//鏈表為空,不刪除assert(*pphead); //指向第一個節點的地址//鏈表不為空,只有一個節點if ((*pphead)->next = NULL) {free(*pphead);*pphead = NULL;return;}//多個節點,釋放最后一個節點,其前驅節點指向空SLTNode* ptail = *pphead;SLTNode* prev = NULL;while (ptail->next) {prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;
}
?????????單鏈表的頭刪

void SLTPopFront(SLTNode** pphead) {assert(pphead);//鏈表為空,不刪除assert(*pphead);//鏈表不為空,釋放最后一個節點,其前驅節點指向空SLTNode* pcur = *pphead;*pphead = pcur->next;pcur->next = NULL;free(pcur);pcur = NULL;
}

?3.1.4?鏈表節點的查找

?????????先對比第一個結點的數據域是否是想要的數據,如果是就直接返回,如果不是則繼續查找下 一個結點,如果到達最后一個結點的時候都沒有匹配的數據,說明要查找數據不存在

//查找
SLTNode* STLFind(SLTNode** pphead, SLTDataType x) {assert(pphead);SLTNode* pcur = *pphead;while (pcur) {if (pcur->data == x) {return pcur;}pcur = pcur->next;}return NULL;
}

3.1.5?在指定位置插入數據

????????在指定位置前插入數據
//在指定位置前插入數據
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {assert(pphead);assert(pos);//頭節點不能為空assert(*pphead);SLTNode* newnode = SLTBuyNode(x);//當pos節點指向 頭節點時,進行頭插if (*pphead == pos) {SLTPushFront(pphead, x);return;}//pos pphead不指向同一節點SLTNode* prev = *pphead;while (prev->next != pos) { //找到pos節點的前驅prev = prev->next;}newnode->next = pos;prev->next = newnode;
}
?????????在指定位置之后插入數據
//在指定位置之后插入數據
void SLTInsertAfter(SLTNode* pos, SLTDataType x) {assert(pos);SLTNode* newnode = SLTBuyNode(x);newnode->next = pos->next;pos->next = newnode;
}

?3.1.6?刪除pos之后的節點

//刪除pos之后的節點
void SLTEraseAfter(SLTNode* pos) {assert(pos);assert(pos->next);//pos后不為空SLTNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;
}

3.1.7?銷毀鏈表

????????重新定義一個指針next,保存pcur指向節點的地址,然后next后移保存下一個節點的地址,然后釋放pcur對應的節點,以此類推,直到pcur為NULL為止?

//銷毀鏈表,由獨立的節點構成,需要一個個銷毀
void SListDestroy(SLTNode** pphead) {assert(pphead);assert(*pphead);SLTNode* pcur = *pphead;while (pcur) {SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

?四、完整代碼

SList.h

?

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//順序表存在的一定的問題:
//1.順序表的中間/頭部的插入需要挪動數據
//2.擴容存在性能的消耗
//3.會有空間的浪費typedef int SLTDataType;
//鏈表是由節點構成
//邏輯結構:一定連續;物理結構:不一定連續
//不會造成空間的浪費,由獨立的節點構成
typedef struct SListNode {  //SList : single linked list 單鏈表SLTDataType data;struct SListNode* next;
}SLTNode;//鏈表的頭插、尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(struct SListNode** pphead, SLTDataType x);//鏈表的頭刪、尾刪
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);//打印鏈表
void SLTPrint(SLTNode* phead);//查找
SLTNode* STLFind(SLTNode** phead, SLTDataType x);//在指定位置前插入數據
void SLTInsert(SLTNode** phead, SLTNode* pos,SLTDataType x);//刪除pos節點
void SLTErase(SLTNode** phead, SLTNode* pos);
//在指定位置之后插入數據
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//刪除pos之后的節點
void SLTEraseAfter(SLTNode* pos);
//銷毀鏈表
void SListDestroy(SLTNode** phead);

SList.c

#include"SList.h"//創建一個節點
struct SListNode* SLTBuyNode(SLTDataType x) {SLTNode* newnode = (struct SListNode*)malloc(sizeof(struct SListNode));newnode->data = x;newnode->next = NULL;return newnode;
}//打印鏈表
void SLTPrint(SLTNode* phead) {SLTNode* pcur = phead;while (pcur) {printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}//鏈表的頭插、尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x) { //一級指針傳遞的為結構體變量地址的值,二級指針接收的是指向結構體變量的指針的地址assert(pphead);SLTNode* newnode = SLTBuyNode(x);//鏈表為空,新節點作為ppheadif (*pphead == NULL) {*pphead = newnode;return;}//鏈表不為空,鏈表的尾指向新節點SLTNode* ptail = *pphead;while (ptail->next) {ptail = ptail->next;}ptail->next = newnode;
}void SLTPushFront(SLTNode** pphead, SLTDataType x) {assert(pphead);//鏈表為空,新節點指向pphead,pphead再指向新節點;鏈表不為空,新節點指向pphead,pphead再指向新節點SLTNode* newnode = SLTBuyNode(x);newnode->next = *pphead;*pphead = newnode;
}//鏈表的頭刪、尾刪
void SLTPopBack(SLTNode** pphead) {assert(pphead);//鏈表為空,不刪除assert(*pphead); //指向第一個節點的地址//鏈表不為空,只有一個節點if ((*pphead)->next = NULL) {free(*pphead);*pphead = NULL;return;}//多個節點,釋放最后一個節點,其前驅節點指向空SLTNode* ptail = *pphead;SLTNode* prev = NULL;while (ptail->next) {prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;
}
void SLTPopFront(SLTNode** pphead) {assert(pphead);//鏈表為空,不刪除assert(*pphead);//鏈表不為空,釋放最后一個節點,其前驅節點指向空SLTNode* pcur = *pphead;*pphead = pcur->next;pcur->next = NULL;free(pcur);pcur = NULL;
}//查找
SLTNode* STLFind(SLTNode** pphead, SLTDataType x) {assert(pphead);SLTNode* pcur = *pphead;while (pcur) {if (pcur->data == x) {return pcur;}pcur = pcur->next;}return NULL;
}//在指定位置前插入數據
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {assert(pphead);assert(pos);//頭節點不能為空assert(*pphead);SLTNode* newnode = SLTBuyNode(x);//當pos節點指向 頭節點時,進行頭插if (*pphead == pos) {SLTPushFront(pphead, x);return;}//pos pphead不指向同一節點SLTNode* prev = *pphead;while (prev->next != pos) { //找到pos節點的前驅prev = prev->next;}newnode->next = pos;prev->next = newnode;
}//刪除pos節點
void SLTErase(SLTNode** pphead, SLTNode* pos) {assert(pphead);assert(pos);//當pos節點指向 頭節點時,進行頭刪if (*pphead == pos) {SLTPopFront(pphead);return;}//pos pphead不指向同一節點SLTNode* prev = *pphead;while (prev->next != pos) { //找到pos節點的前驅prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;
}//在指定位置之后插入數據
void SLTInsertAfter(SLTNode* pos, SLTDataType x) {assert(pos);SLTNode* newnode = SLTBuyNode(x);newnode->next = pos->next;pos->next = newnode;
}//刪除pos之后的節點
void SLTEraseAfter(SLTNode* pos) {assert(pos);assert(pos->next);//pos后不為空SLTNode* del = pos->next;pos->next = pos->next->next;//pos->next = del->next;free(del);del = NULL;
}//銷毀鏈表,由獨立的節點構成,需要一個個銷毀
void SListDestroy(SLTNode** pphead) {assert(pphead);assert(*pphead);SLTNode* pcur = *pphead;while (pcur) {SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

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

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

相關文章

尚硅谷爬蟲note15n

1. 多條管道 多條管道開啟&#xff08;2步&#xff09;&#xff1a; (1)定義管道類 &#xff08;2&#xff09;在settings中開啟管道 在pipelines中&#xff1a; import urllib.request # 多條管道開啟 #(1)定義管道類 #&#xff08;2&#xff09;在setti…

oracle檢查字段為空

在Oracle數據庫中&#xff0c;檢查字段是否為空通常涉及到使用IS NULL條件。如果你想查詢某個表中的字段是否為空&#xff0c;你可以使用SELECT語句結合WHERE子句來實現。這里有一些基本示例來展示如何進行這樣的查詢。 示例1: 檢查單個字段是否為空 假設你有一個表employees…

虛幻基礎:動畫層接口

文章目錄 動畫層&#xff1a;動畫圖表中的函數接口&#xff1a;名字&#xff0c;沒有實現。動畫層接口&#xff1a;由動畫藍圖實現1.動畫層可直接調用實現功能2.動畫層接口必須安裝3.動畫層默認使用本身實現4.動畫層也可使用其他動畫藍圖實現&#xff0c;但必須在角色藍圖中關聯…

HarmonyOS學習第18天:多媒體功能全解析

一、開篇引入 在當今數字化時代&#xff0c;多媒體已經深度融入我們的日常生活。無論是在工作中通過視頻會議進行溝通協作&#xff0c;還是在學習時借助在線課程的音頻講解加深理解&#xff0c;亦或是在休閑時光用手機播放音樂放松身心、觀看視頻打發時間&#xff0c;多媒體功…

緒論數據結構基本概念(刷題筆記)

&#xff08;一&#xff09;單選題 1.與數據元素本身的形式、相對位置和個數無關的是&#xff08;B&#xff09;【廣東工業大學2019年829數據結構】 A.數據存儲結構 B.數據邏輯結構 C.算法 D.操作 2.在數據結構的討論中把數據結構從邏輯上分為&#xff08;C&#xff09;【中國…

GPTQ - 生成式預訓練 Transformer 的精確訓練后壓縮

GPTQ - 生成式預訓練 Transformer 的精確訓練后壓縮 flyfish 曾經是 https://github.com/AutoGPTQ/AutoGPTQ 現在是https://github.com/ModelCloud/GPTQModel 對應論文是 《Accurate Post-Training Quantization for Generative Pre-trained Transformers》 生成式預訓練Tr…

git的使用方法

文章目錄 前言git簡介GIT的基本操作克隆倉庫 (Clone)獲取最新代碼 (Pull)提交代碼到遠程倉庫查看當前分支查看提交代碼的日志git config 配置用戶信息 GIT的實操 前言 git是一種軟件版本管理工具&#xff0c;在多人團隊軟件開發中地方非常重要。 類似與SVN&#xff0c;git工具…

php虛擬站點提示No input file specified時的問題及權限處理方法

訪問站點&#xff0c;提示如下 No input file specified. 可能是文件權限有問題&#xff0c;也可能是“.user.ini”文件路徑沒有配置對&#xff0c;最簡單的辦法就是直接將它刪除掉&#xff0c;還有就是將它設置正確 #配置成自己服務器上正確的路徑 open_basedir/mnt/qiy/te…

使用Langflow和AstraDB構建AI助手:從架構設計到與NocoBase的集成

本文由 Leandro Martins 編寫&#xff0c;最初發布于 Building an AI Assistant with Langflow and AstraDB: From Architecture to Integration with NocoBase。 引言 本文的目標是演示如何創建一個集成了 NocoBase、LangFlow 和 VectorDB 工具的 AI 助手。作為基礎&#xf…

6.聊天室環境安裝 - Ubuntu22.04 - elasticsearch(es)的安裝和使用

目錄 介紹安裝安裝kibana安裝ES客戶端使用 介紹 Elasticsearch&#xff0c; 簡稱 ES&#xff0c;它是個開源分布式搜索引擎&#xff0c;它的特點有&#xff1a;分布式&#xff0c;零配置&#xff0c;自動發現&#xff0c;索引自動分片&#xff0c;索引副本機制&#xff0c;res…

SSL VXN

SSL VPN是采用SSL&#xff08;Security Socket Layer&#xff09;/TLS&#xff08;Transport Layer Security&#xff09;協議來實現遠程接入的一種輕量級VPN技術,其基于B/S架構&#xff0c;免于安裝客戶端&#xff0c;相較與IPSEC有更高的靈活度和管理性&#xff0c;當隧道建立…

【Qt】成員函數指針

一、成員函數指針的本質 與普通函數指針的區別&#xff1a; // 普通函數指針 void (*funcPtr)() &普通函數;// 成員函數指針 void (MyClass::*memberFuncPtr)() &MyClass::成員函數;? 綁定對象&#xff1a;成員函數指針必須與類的實例對象結合使用 ? 隱含 this 指…

通義萬相2.1開源版本地化部署攻略,生成視頻再填利器

2025 年 2 月 25 日晚上 11&#xff1a;00 通義萬相 2.1 開源發布&#xff0c;前兩周太忙沒空搞它&#xff0c;這個周末&#xff0c;也來本地化部署一個&#xff0c;體驗生成效果如何&#xff0c;總的來說&#xff0c;它在國內文生視頻、圖生視頻的行列處于領先位置&#xff0c…

Linux——system V共享內存

共享內存區是最快的IPC(進程內通信)形式&#xff0c;不再通過執行進入內核的系統調用來傳遞彼此的數據 1.共享內存的原理 IPC通信的本質是讓不同的進程先看到同一份資源&#xff0c;然后再進行通信&#xff0c;所以想要通過共享內存進行通信&#xff0c;那么第一步一定是讓兩個…

01 SQl注入基礎步驟(數字、字符、布爾盲注、報錯)

目錄 1、SQL注入漏洞的概要 2、SQL注入的常規思路 3、數字型注入 4、字符型注入 5、布爾盲注 6、報錯注入 1、SQL注入漏洞的概要 原理&#xff1a;通過用戶輸入的數據未嚴格過濾&#xff0c;將惡意SQL語句拼接到原始查詢中&#xff0c;從而操控數據庫執行非預期操作。 …

leetcode-sql數據庫面試題沖刺(高頻SQL五十題)

題目&#xff1a; 620.有趣的電影 表&#xff1a;cinema ------------------------ | Column Name | Type | ------------------------ | id | int | | movie | varchar | | description | varchar | | rating | float | ------------------------ id 是該表的主鍵(具有唯一值…

7.2 奇異值分解的基與矩陣

一、奇異值分解 奇異值分解&#xff08;SVD&#xff09;是線性代數的高光時刻。 A A A 是一個 m n m\times n mn 的矩陣&#xff0c;可以是方陣或者長方形矩陣&#xff0c;秩為 r r r。我們要對角化 A A A&#xff0c;但并不是把它化成 X ? 1 A X X^{-1}A X X?1AX 的形…

在本地部署DeepSeek等大模型時,需警惕的潛在安全風險

在本地部署DeepSeek等大模型時&#xff0c;盡管數據存儲在本地環境&#xff08;而非云端&#xff09;&#xff0c;但仍需警惕以下潛在安全風險&#xff1a; 1. 模型與數據存儲風險 未加密的存儲介質&#xff1a;若訓練數據、模型權重或日志以明文形式存儲&#xff0c;可能被物…

【javaEE】多線程(進階)

1.????前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 親愛的朋友們&#x1f44b;&#x1f44b;&#xff0c;這里是E綿綿呀????。 如果你喜歡這篇文章&#xff0c;請別吝嗇你的點贊????和收藏&#x1f4d6;&#x1f4d6;。如果你對我的…

dify中使用NL2SQL

在 Dify 工作流中融入 NL2SQL&#xff08;自然語言轉 SQL&#xff09;之能力&#xff0c;可依循如下步驟達成&#xff0c;借由 Dify 的模塊化設計以及模型編排之功能&#xff0c;優化數據庫查詢之智能化交互&#xff1a; 一、環境準備與 Dify 部署 安裝 Docker 與 Dify 務須確…