=========================================================================
相關(guān)代碼gitee自取:
C語言學(xué)習(xí)日記: 加油努力 (gitee.com)
?=========================================================================
接上期:
【數(shù)據(jù)結(jié)構(gòu)初階】二、 線性表里的順序表(C語言實(shí)現(xiàn)順序表)-CSDN博客
?=========================================================================
? ? ? ? ? ? ? ? ? ?
引言
通過上期對順序表的介紹和使用
我們可以知道順序表有以下優(yōu)點(diǎn)和缺點(diǎn):
? ? ? ? ? ??文章來源:http://www.zghlxwxcb.cn/news/detail-715785.html
順序表優(yōu)點(diǎn)
? ? ? ? ? ? ? ? ??
- 尾插 和 尾刪 操作足夠快
? ? ? ? ? ? ? ?- 能夠使用下標(biāo)的隨機(jī)訪問和修改,這是很方便的
? ? ? ? ? ??
-----------------------------------------------------------------------------------------------
? ? ? ? ? ??
順序表缺點(diǎn)
? ? ? ? ? ? ? ? ??
- 頭部/中間的插入和刪除操作較慢,時(shí)間復(fù)雜度為O(N)
? ? ? ? ? ? ? ? ? ? ? ? ??- 增容需要申請新空間,拷貝數(shù)據(jù),釋放舊空間。會(huì)有不小的消耗。
? ? ? ? ? ? ? ? ? ? ?- 增容一般是呈2倍的增長,勢必會(huì)有一定的空間浪費(fèi)。
例如:
當(dāng)前容量為100,滿了以后增容到 200,我們再繼續(xù)插入了5個(gè)數(shù)據(jù),
后面沒有數(shù)據(jù)插入了,那么就浪費(fèi)了95個(gè)數(shù)據(jù)空間。? ? ? ? ? ??
? ? ? ? ? ??
下面將要介紹和實(shí)現(xiàn)的鏈表就不會(huì)出現(xiàn)這些問題
? ? ? ? ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
? ? ? ? ? ? ?
1 . 鏈表
(1). 鏈表的概念及結(jié)構(gòu):
? ? ? ? ? ?
鏈表的概念
鏈表是一種物理存儲(chǔ)結(jié)構(gòu)上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),
數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接次序實(shí)現(xiàn)的,
單鏈表一般不會(huì)單獨(dú)使用,
只有頭插和頭刪實(shí)現(xiàn)簡單且效率高? ? ? ? ? ? ??
鏈表的結(jié)構(gòu)
- 鏈表也屬于線性表,線性表在物理上存儲(chǔ)時(shí),
通常以數(shù)組(順序表)和鏈?zhǔn)浇Y(jié)構(gòu)(鏈表)的形式存儲(chǔ),
鏈?zhǔn)浇Y(jié)構(gòu)在邏輯上是連續(xù)的,但在物理上不一定連續(xù)
? ? ? ? ? ? ? ? ?- 現(xiàn)實(shí)中的結(jié)點(diǎn)一般是從堆上申請出來的
? ? ? ? ? ? ??- 從堆上申請的空間,是按照一定的策略來分配的,
兩次申請的空間可能連續(xù),也可能不連續(xù)圖示:
? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ??
(2). 鏈表的分類:
? ? ? ? ? ??
實(shí)際中鏈表的結(jié)構(gòu)非常多樣,
以下情況組合起來就有8種鏈表結(jié)構(gòu):? ? ? ? ? ?
單向 或 雙向 鏈表
單向鏈表圖示
? ? ? ? ? ? ? ??
雙向鏈表圖示
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------文章來源地址http://www.zghlxwxcb.cn/news/detail-715785.html
? ? ? ? ? ??
帶頭(哨兵位)?或 不帶頭(哨兵位) 鏈表
帶頭鏈表圖示
? ? ? ? ? ? ? ?
不帶頭鏈表圖示
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
循環(huán) 或 非循環(huán) 鏈表
循環(huán)鏈表圖示
? ? ? ? ? ? ? ? ??
非循環(huán)鏈表圖示
? ? ? ? ? ??
? ? ? ? ? ??
雖然有這么多的鏈表的結(jié)構(gòu),
但是我們實(shí)際中最常用還是兩種結(jié)構(gòu):無頭單向非循環(huán)鏈表 和?帶頭雙向循環(huán)鏈表
? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ??
(3). 兩種常用鏈表結(jié)構(gòu):
? ? ? ? ? ??
無頭單向非循環(huán)鏈表
簡介:
結(jié)構(gòu)簡單,一般不會(huì)單獨(dú)用來存數(shù)據(jù)。
實(shí)際中更多是作為其他數(shù)據(jù)結(jié)構(gòu)的子結(jié)構(gòu),
如哈希桶、圖的鄰接表等等。
另外這種結(jié)構(gòu)在筆試面試中出現(xiàn)很多。
? ? ? ? ??
圖示:
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
帶頭雙向循環(huán)鏈表
簡介:
結(jié)構(gòu)最復(fù)雜,一般用在單獨(dú)存儲(chǔ)數(shù)據(jù)。
實(shí)際中使用的鏈表數(shù)據(jù)結(jié)構(gòu),都是帶頭雙向循環(huán)鏈表。
另外這個(gè)結(jié)構(gòu)雖然結(jié)構(gòu)復(fù)雜,
但是使用代碼實(shí)現(xiàn)以后會(huì)發(fā)現(xiàn)結(jié)構(gòu)會(huì)帶來很多優(yōu)勢,實(shí)現(xiàn)反而簡單了
? ? ? ? ??
圖示:
? ? ? ? ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
? ? ? ? ? ? ?
2 . 鏈表的實(shí)現(xiàn) (無頭+單向+非循環(huán)鏈表)
(詳細(xì)解釋在圖片的注釋中,代碼分文件放最后)
? ? ? ? ? ? ? ? ??
實(shí)現(xiàn)具體功能前的準(zhǔn)備工作
- 創(chuàng)建單鏈表數(shù)據(jù)類型 -- 鏈表結(jié)點(diǎn)中數(shù)據(jù)域里存儲(chǔ)的數(shù)據(jù)的類型
? ? ? ? ? ? ? ??- 創(chuàng)建單鏈表結(jié)點(diǎn)結(jié)構(gòu)體(類型) -- 結(jié)點(diǎn)中應(yīng)有 數(shù)據(jù)域 和 指針域
圖示
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
PrintSList函數(shù) -- 遍歷打印鏈表
- 需要使用到鏈表頭指針phead,為了后續(xù)使用又不能改變頭指針,所以要代替頭指針
? ? ? ? ? ? ? ? ?- 因?yàn)?span style="color:#fe2c24;">鏈表到最后結(jié)點(diǎn)里指針域的NULL(0x00000000)才結(jié)束,
所以可以使用while循環(huán)進(jìn)行遍歷并打印
? ? ? ? ? ? ??- 最后提示已指向NULL指針鏈表,遍歷結(jié)束
圖示
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
SLTNode函數(shù) -- 生成鏈表的結(jié)點(diǎn)
- 開辟結(jié)點(diǎn)所需的動(dòng)態(tài)空間,要注意一個(gè)結(jié)點(diǎn)大小要看數(shù)據(jù)域和指針域的大小
? ? ? ? ? ?- 檢查空間是否開辟成功
? ? ? ? ? ?- 將要放入結(jié)點(diǎn)數(shù)據(jù)域的數(shù)據(jù)放入
? ? ? ? ? ?- 設(shè)置新創(chuàng)建結(jié)點(diǎn)的指針域
? ? ? ? ? ?- 返回創(chuàng)建的新結(jié)點(diǎn)的指針(地址)
圖示
測試 --?PrintSList、SLTNode函數(shù)
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
SLTPushBack函數(shù)(難) -- 向鏈表尾部插入一個(gè)結(jié)點(diǎn)(尾插)
- 因?yàn)?span style="color:#fe2c24;">要插入一個(gè)結(jié)點(diǎn),所以先使用BuySListNode函數(shù)創(chuàng)建一個(gè)結(jié)點(diǎn)
? ? ? ? ? ?- 尾插時(shí)要判斷鏈表是否已經(jīng)有結(jié)點(diǎn),
如果沒有結(jié)點(diǎn),
則將新創(chuàng)建結(jié)點(diǎn)的地址賦給頭指針,
因?yàn)槭?span style="color:#fe2c24;">對指針本身進(jìn)行操作,所以用二級指針對頭指針進(jìn)行操作
? ? ? ? ? ?- 如果已經(jīng)有了結(jié)點(diǎn),
則先創(chuàng)建一個(gè)指針替代頭指針,
(這里是對頭指針直線的結(jié)點(diǎn)結(jié)構(gòu)體進(jìn)行操作,所以用指針就夠了,不需要二級指針)
再使用while循環(huán)獲得最后一個(gè)結(jié)點(diǎn)的地址,
最后將尾插的結(jié)點(diǎn)連接起來圖示
(可以結(jié)合單鏈表物理圖來理解)
↓↓↓↓
測試 --?SLTPushBack函數(shù)
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
SLTPushFront函數(shù) --?向鏈表頭部插入一個(gè)結(jié)點(diǎn)(頭插)
- 因?yàn)?span style="color:#fe2c24;">要插入一個(gè)結(jié)點(diǎn),所以先使用BuySListNode函數(shù)創(chuàng)建一個(gè)結(jié)點(diǎn)
? ? ? ? ? ?- 使用plist把原本頭結(jié)點(diǎn)地址賦給新插入頭結(jié)點(diǎn)指針域
? ? ? ? ? ?- 再把頭指針改為指向新插入頭結(jié)點(diǎn)
圖示
測試 -- SLTPushFront函數(shù)
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
SLTPopBack函數(shù) --?刪除鏈表尾部結(jié)點(diǎn)(尾刪)
- 鏈表尾刪要考慮三種情況:
第一種情況:鏈表為空,沒有任何結(jié)點(diǎn)
這種情況直接assert斷言即可
? ? ? ? ? ?- 第二種情況:鏈表只有一個(gè)結(jié)點(diǎn)
這種情況要釋放掉唯一的結(jié)點(diǎn)
? ? ? ? ? ?- 第三種情況:鏈表有一個(gè)以上結(jié)點(diǎn)
這種情況需要兩個(gè)指針來進(jìn)行操作圖示
測試 -- SLTPopBack函數(shù)
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
SLTPopFront函數(shù) --?刪除鏈表頭部結(jié)點(diǎn)(頭刪)
- 頭刪分兩種情況就夠了:空鏈表 和 非空鏈表
? ? ? ? ? ?- 空鏈表頭刪:
直接assert斷言pass掉
(莫名戳中笑點(diǎn)哈哈哈哈哈哈哈哈)
? ? ? ? ? ?- 非空鏈表頭刪:
先通過plist頭指針獲得第二個(gè)結(jié)點(diǎn)地址,方便頭刪后讓原本的第二個(gè)結(jié)點(diǎn)做新的頭結(jié)點(diǎn)
再free釋放掉頭指針,
最后讓plist頭指針指向原本的第二個(gè)結(jié)點(diǎn)做新的頭結(jié)點(diǎn)圖示
測試 -- SLTPopFront函數(shù)
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
SLTFind函數(shù) --?查找指定值(x)所在結(jié)點(diǎn)的地址
- 創(chuàng)建一個(gè)變量代替頭指針
? ? ? ? ? ?- 使用while循環(huán)在鏈表中進(jìn)行遍歷,查找指定值(x)
圖示
測試 -- SLTFind函數(shù)
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
SLTInsert函數(shù) --?在鏈表指定位置(pos)之前插入一個(gè)值(x)
- 分三種情況討論:
鏈表為空(空鏈表)、在第一個(gè)結(jié)點(diǎn)前插入(頭插)、非頭插
? ? ? ? ? ?- 鏈表為空:
直接assert斷言pass掉
? ? ? ? ? ?- 在第一個(gè)結(jié)點(diǎn)前插入(頭插):
復(fù)用頭插SLTPushFront函數(shù)進(jìn)行插入
? ? ? ? ? ?- 非頭插:
使用鏈表頭指針找到pos結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn)地址prev- 再創(chuàng)建一個(gè)新結(jié)點(diǎn)newnode
將新結(jié)點(diǎn)newnode插入pos結(jié)點(diǎn)之前,prev結(jié)點(diǎn)之后圖示
測試 -- SLTInsert函數(shù)
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
SLTInsertAfter函數(shù) --?在鏈表指定位置(pos)之后插入一個(gè)值(x)
- 如果接收的pos結(jié)點(diǎn)地址為空,無法繼續(xù)操作
要用assert斷言繼續(xù)處理
? ? ? ? ? ?- 因?yàn)橐?span style="color:#fe2c24;">在鏈表中插入一個(gè)值,
所以要用BuySListNode函數(shù)新增一個(gè)結(jié)點(diǎn)
? ? ? ? ? ?- 先讓newnode結(jié)點(diǎn)的指針域next指向后一個(gè)結(jié)點(diǎn)
? ? ? ? ? ?- 再讓pos結(jié)點(diǎn)指向newnode結(jié)點(diǎn),
將鏈表連接起來圖示
測試 -- SLTInsertAfter函數(shù)
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
SLTErase函數(shù) --?在鏈表刪除指定位置(pos)結(jié)點(diǎn)
- 為防止要?jiǎng)h的pos結(jié)點(diǎn)指針為空指針,
使用assert斷言
? ? ? ? ? ?- 分兩種情況:
頭刪 和 非頭刪
? ? ? ? ? ?- 頭刪:
直接復(fù)用頭刪SLTPopFront函數(shù)
? ? ? ? ? ?- 非頭刪:
找到pos結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn)prev,
將 prev結(jié)點(diǎn) 和 pos結(jié)點(diǎn)后一個(gè)結(jié)點(diǎn) 連接起來
? ? ? ? ? ?- 最后釋放(刪除)pos結(jié)點(diǎn)完成刪除操作
圖示
測試 -- SLTErase函數(shù)
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
SLTEraseAfter函數(shù) --?在鏈表刪除指定位置(pos)之后一個(gè)結(jié)點(diǎn)
- 先使用assert斷言分別排除pos結(jié)點(diǎn)指針為空和pos結(jié)點(diǎn)為尾結(jié)點(diǎn)的情況,
我們是刪除pos結(jié)點(diǎn)的后一個(gè)結(jié)點(diǎn),
如果pos為尾結(jié)點(diǎn),就會(huì)無法操作
? ? ? ? ? ?- 將要?jiǎng)h除的pos結(jié)點(diǎn)下個(gè)結(jié)點(diǎn)posNext地址先保存起來
? ? ? ? ? ?- 再把pos結(jié)點(diǎn)和下下個(gè)結(jié)點(diǎn)連接起來
? ? ? ? ? ?- 最后釋放(刪除)posNext結(jié)點(diǎn)
圖示
測試 -- SLTEraseAfter函數(shù)
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
? ? ? ? ? ??
SLTDestroy函數(shù) --?銷毀鏈表釋放開辟的動(dòng)態(tài)空間
- 因?yàn)?span style="color:#fe2c24;">鏈表釋放后,還要把頭指針plist給置為空,
要操作到plist,所以要用到二級指針
? ? ? ? ? ?- 進(jìn)行assert斷言,
pphead二級指針不為空
(以上函數(shù)中參數(shù)帶二級指針pphead的都需要進(jìn)行此操作)
? ? ? ? ? ?- 創(chuàng)建一個(gè)指針進(jìn)行遍歷釋放空間
? ? ? ? ? ?- 最后將鏈表頭指針置為空
圖示
? ? ? ? ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
? ? ? ? ? ? ?
3 . 對應(yīng)代碼
SList.h?-- 單鏈表頭文件
//鏈表頭文件: #pragma once //先包含之后要用到的頭文件: #include <stdio.h> #include <stdlib.h> #include <assert.h> //重命名一個(gè)單鏈表數(shù)據(jù)類型 typedef int SLTDataType; //SLT -- single link table //創(chuàng)建一個(gè)單鏈表結(jié)點(diǎn)結(jié)構(gòu)體 //這里數(shù)據(jù)域是int類型4字節(jié),指針域指針也是4個(gè)字節(jié), //所以一個(gè)結(jié)點(diǎn)就是8個(gè)字節(jié) typedef struct SListNode { //結(jié)點(diǎn)數(shù)據(jù)域: SLTDataType data; //結(jié)點(diǎn)指針域: //因?yàn)槭侵赶騿捂湵斫Y(jié)點(diǎn)結(jié)構(gòu)體的指針, //所以是單鏈表結(jié)點(diǎn)結(jié)構(gòu)體類型的指針 struct SListNode* next; //第一個(gè)結(jié)點(diǎn)要找到第二個(gè)結(jié)點(diǎn),物理空間不連續(xù) //通過上個(gè)結(jié)點(diǎn)指向下個(gè)結(jié)點(diǎn),實(shí)現(xiàn)邏輯連續(xù) }SLTNode; //將struct SListNode重命名為SLTNode //創(chuàng)建遍歷打印單鏈表的函數(shù) -- 接收單鏈表頭指針 void PrintSList(SLTNode* phead); //創(chuàng)建生成結(jié)點(diǎn)的函數(shù) -- 接收要存入結(jié)點(diǎn)數(shù)據(jù)域的數(shù)據(jù);返回該結(jié)點(diǎn)的指針 SLTNode* BuySListNode(SLTDataType x); //創(chuàng)建尾插函數(shù) -- //使用二級指針接收單鏈表頭指針的地址 和 接收要插入的值 void SLTPushBack(SLTNode** pphead, SLTDataType x); //創(chuàng)建頭插函數(shù) -- //使用二級指針接收單鏈表頭指針的地址 和 接收要插入的值 void SLTPushFront(SLTNode** pphead, SLTDataType x); //創(chuàng)建尾刪函數(shù) -- //使用二級指針接收單鏈表頭指針的地址 void SLTPopBack(SLTNode** pphead); //創(chuàng)建頭刪函數(shù) -- //使用二級指針接收單鏈表頭指針的地址 void SLTPopFront(SLTNode** pphead); //創(chuàng)建查找函數(shù) -- //查找指定值(x)所在結(jié)點(diǎn)的地址 //接收 鏈表頭指針phead、查找值x SLTNode* SLTFind(SLTNode* phead, SLTDataType x); //創(chuàng)建指定位置插入函數(shù)1 -- //在鏈表指定位置(pos)之前插入一個(gè)值(x) //接收 鏈表頭指針地址pphead、指定結(jié)點(diǎn)指針pos、插入值x void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x); //創(chuàng)建指定位置插入函數(shù)2 -- //在鏈表指定位置(pos)之后插入一個(gè)值(x) //接收 指定結(jié)點(diǎn)指針pos、插入值x void SLTInsertAfter(SLTNode* pos, SLTDataType x); //創(chuàng)建刪除指定結(jié)點(diǎn)函數(shù)1 -- //在鏈表刪除指定位置(pos)結(jié)點(diǎn) ///接收 鏈表頭指針地址pphead、指定結(jié)點(diǎn)指針pos void SLTErase(SLTNode** pphead, SLTNode* pos); //創(chuàng)建刪除指定結(jié)點(diǎn)函數(shù)2 -- //在鏈表刪除指定位置(pos)之后一個(gè)結(jié)點(diǎn) //接收 指定結(jié)點(diǎn)指針pos void SLTEraseAfter(SLTNode* pos); //創(chuàng)建銷毀鏈表函數(shù): void SLTDestroy(SLTNode** pphead);
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
SList.c?-- 單鏈表函數(shù)實(shí)現(xiàn)文件
//鏈表實(shí)現(xiàn)文件: #define _CRT_SECURE_NO_WARNINGS 1 //包含鏈表頭文件: #include "SList.h" //創(chuàng)建遍歷單鏈表的函數(shù):接收單鏈表結(jié)點(diǎn)的頭指針 void PrintSList(SLTNode* phead) { //phead頭指針指向第一個(gè)結(jié)點(diǎn), //之后還要多次使用phead頭指針, //所以不要改變phead頭指針, //所以可以創(chuàng)建一個(gè)和phead頭指針一樣類型的指針cur代替phead頭指針 SLTNode* cur = phead; //因?yàn)殒湵淼阶詈蠼Y(jié)點(diǎn)的指針域?yàn)镹ULL才結(jié)束 //所以只要cur不為NULL就繼續(xù)遍歷結(jié)點(diǎn)進(jìn)行打?。? while (cur != NULL) { //通過cur當(dāng)前指向的結(jié)點(diǎn)打印cur里數(shù)據(jù)域的內(nèi)容: printf("%d->", cur->data); //通過結(jié)點(diǎn)里指針域指向下一個(gè)結(jié)點(diǎn),方便打印下個(gè)結(jié)點(diǎn)的數(shù)據(jù)域內(nèi)容 cur = cur->next; } //最后提示已指向NULL指針鏈表,遍歷結(jié)束: printf("NULL\n"); } //創(chuàng)建生成結(jié)點(diǎn)的函數(shù) -- 接收要存入結(jié)點(diǎn)數(shù)據(jù)域的數(shù)據(jù);返回該結(jié)點(diǎn)的指針 SLTNode* BuySListNode(SLTDataType x) { //給結(jié)點(diǎn)開辟動(dòng)態(tài)空間(注意開辟空間的大小是SLTNode結(jié)點(diǎn)的大小--8個(gè)字節(jié)) SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode)); //返回該結(jié)點(diǎn)地址 //檢查是否開辟成功: if (newnode == NULL) //返回空指針,說明開辟失敗 { //打印錯(cuò)誤信息: perror("malloc fail"); //開辟失敗直接退出程序: exit(-1); } //將接收的值x賦給該結(jié)點(diǎn)的數(shù)據(jù)域: newnode->data = x; //設(shè)置新創(chuàng)建結(jié)點(diǎn)的指針域: //因?yàn)槭亲钚碌慕Y(jié)點(diǎn),即最尾部的結(jié)點(diǎn), //所以指針域的指針應(yīng)是NULL(鏈表末尾結(jié)束) //之后通過指針域連接各個(gè)結(jié)點(diǎn)的操作要看情況操作,先都初始化為NULL newnode->next = NULL; //返回該新結(jié)點(diǎn)的指針(地址) return newnode; } //創(chuàng)建尾插函數(shù) //使用二級指針接收單鏈表頭指針的地址 和 接收要插入的值 void SLTPushBack(SLTNode** pphead, SLTDataType x) { //改變結(jié)構(gòu)體,要用結(jié)構(gòu)體指針 //改變結(jié)構(gòu)體指針,要用結(jié)構(gòu)體指針的指針(二級指針) //因?yàn)閜phead二級指針存儲(chǔ)的是plist指針, //plist指針指向的值可能為空,但指針的地址不能為空 //pphead是plist的地址,正常情況下一定不為空 assert(pphead); //先使用newnode函數(shù)為要尾插的值創(chuàng)建一個(gè)結(jié)點(diǎn) //并返回該結(jié)點(diǎn)地址 SLTNode* newnode = BuySListNode(x); //判斷phead是否是NULL,如果是說明鏈表還沒開辟過一個(gè)結(jié)點(diǎn) if (*pphead == NULL) { //如果為空,則將上面開辟的newnode結(jié)點(diǎn)地址賦給phead *pphead = newnode; //要改變結(jié)構(gòu)體的指針,就要用二級指針 //這里pphead是二級指針,存放鏈表頭指針plist的地址 //因?yàn)槿绻靡患壷羔楽LTNode* phead的話, //phead形參只是plist實(shí)參的臨時(shí)拷貝,兩者空間相互獨(dú)立 //改變phead的話不會(huì)改變plist,plist還會(huì)是空指針 //所以要用二級指針pphead存放plist指針的地址, //*pphead解引用就能得到一級指針plist, //即 *pphead = plist //所以實(shí)際上上面的代碼就相當(dāng)于:plist = newnode //這樣就讓本來指向空指針的頭指針指向了新創(chuàng)建的結(jié)點(diǎn)指針 //鏈表就能夠連接起來 } else //不為空則往尾部插入新結(jié)點(diǎn): { //創(chuàng)建另一個(gè)指針存放單鏈表頭指針 SLTNode* tail = *pphead; //*pphead == plist //使用while循環(huán)獲得最后一個(gè)結(jié)點(diǎn)的地址 while (tail->next != NULL) //如果下個(gè)結(jié)點(diǎn)的next指針域不為NULL, //則繼續(xù)往后找,直到tail等于最后結(jié)點(diǎn)的地址 { //把當(dāng)前結(jié)點(diǎn)指針域里下個(gè)結(jié)點(diǎn)的地址給到tail, //進(jìn)行while循環(huán)下次判斷: tail = tail->next; } //tail找到最后結(jié)點(diǎn)地址后, //把尾插的新結(jié)點(diǎn)地址給到tail的next指針域, //連接起鏈表: tail->next = newnode; //要改變結(jié)構(gòu)體,用結(jié)構(gòu)體的指針即可 //因?yàn)閚ewnode、tail、pphead都是臨時(shí)(局部)變量, //所以函數(shù)運(yùn)行結(jié)束后都會(huì)銷毀, //但malloc函數(shù)開辟的空間(結(jié)點(diǎn))都在堆上不會(huì)銷毀 //通過解引用二級指針pphead改變plist也沒有問題 } } //創(chuàng)建頭插函數(shù) -- //使用二級指針接收單鏈表頭指針的地址 和 接收要插入的值 void SLTPushFront(SLTNode** pphead, SLTDataType x) { //因?yàn)閜phead二級指針存儲(chǔ)的是plist指針, //plist指針指向的值可能為空,但指針的地址不能為空 //pphead是plist的地址,正常情況下一定不為空 assert(pphead); //先使用newnode函數(shù)為要頭插的值創(chuàng)建一個(gè)結(jié)點(diǎn) //并返回該結(jié)點(diǎn)地址 SLTNode* newnode = BuySListNode(x); //因?yàn)橐惨玫芥湵眍^指針本身,所以也要使用二級指針 //因?yàn)閜list指向原本頭結(jié)點(diǎn)地址, //所以可以使用plist把原本頭結(jié)點(diǎn)地址賦給新插入頭結(jié)點(diǎn)指針域: newnode->next = *pphead; //再把頭指針改為指向新插入頭結(jié)點(diǎn): *pphead = newnode; } //創(chuàng)建尾刪函數(shù) -- //使用二級指針接收單鏈表頭指針的地址 void SLTPopBack(SLTNode** pphead) { //尾刪需要考慮三種情況: //注:*pphead 就是 plist //因?yàn)閜phead二級指針存儲(chǔ)的是plist指針, //plist指針指向的值可能為空,但指針的地址不能為空 //pphead是plist的地址,正常情況下一定不為空 assert(pphead); // 第一種情況:鏈表為空 -- 沒有任何結(jié)點(diǎn) //沒有結(jié)點(diǎn)那就刪不了了,使用assert接收到空指針就報(bào)警告 assert(*pphead); // 第二種情況:鏈表只有一個(gè)結(jié)點(diǎn) if ((*pphead)->next == NULL) // -> 也是解引用(結(jié)構(gòu)體的),優(yōu)先級比 * 高 //所以 *pphead 要用() 括起來 { //只有一個(gè)結(jié)點(diǎn),又要尾刪,那就直接把這唯一的結(jié)點(diǎn)刪掉: free(*pphead); //直接free釋放掉plist指向的結(jié)點(diǎn) //再把釋放掉的plist置為空指針,防止成為野指針: *pphead = NULL; } else // 第三種情況:鏈表有一個(gè)以上結(jié)點(diǎn) { //這種情況額外兩個(gè)指針, //一個(gè)tail指針 -- 用來找到最后一個(gè)結(jié)點(diǎn)地址并將其釋放, //還有一個(gè)tailPrev指針 -- 指向tail指針的前一個(gè)結(jié)點(diǎn)地址, //用來改變該結(jié)點(diǎn)的指針域, //防止原本指向tail結(jié)點(diǎn)的指針域變成野指針 //先替代頭指針plist: SLTNode* tail = *pphead; //創(chuàng)建tailPrev指針, //之后操作會(huì)指向tail結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn), //即倒數(shù)第二個(gè)結(jié)點(diǎn) SLTNode* tailPrev = NULL; //再使用while循環(huán)找到尾結(jié)點(diǎn): //和尾插的操作類似 while (tail->next != NULL) { //tail查找之前先把當(dāng)前指向結(jié)點(diǎn)地址給tailPrev //這樣最后tail會(huì)指向尾結(jié)點(diǎn), //tailPrev會(huì)指向倒數(shù)第二個(gè)結(jié)點(diǎn) tailPrev = tail; tail = tail->next; } //結(jié)束while循環(huán)后tail指向尾結(jié)點(diǎn)地址, //因?yàn)槭俏矂h,所以free釋放掉tail就可以“刪掉”尾結(jié)點(diǎn) free(tail); //因?yàn)閠ail是局部(臨時(shí))變量,出了函數(shù)就銷毀, //所以不置為指針也可以,不用擔(dān)心成為野指針 //刪除原尾結(jié)點(diǎn)后,倒數(shù)第二個(gè)結(jié)點(diǎn)成為尾結(jié)點(diǎn), //要把其指針域next置為空指針,成為鏈表新結(jié)尾 tailPrev->next = NULL; } } //創(chuàng)建頭刪函數(shù) -- //使用二級指針接收單鏈表頭指針的地址 void SLTPopFront(SLTNode** pphead) { //因?yàn)閜phead二級指針存儲(chǔ)的是plist指針, //plist指針指向的值可能為空,但指針的地址不能為空 //pphead是plist的地址,正常情況下一定不為空 assert(pphead); //分兩種情況: //第一種情況:鏈表里沒有結(jié)點(diǎn)(空鏈表) //沒有結(jié)點(diǎn)可以刪,直接assert斷言pass掉: assert(*pphead); //第二種情況:鏈表有一個(gè)和多個(gè)結(jié)點(diǎn)(非空鏈表) //因?yàn)槭莿h掉頭結(jié)點(diǎn),所以不用考慮找尾結(jié)點(diǎn) //所以不用細(xì)分一個(gè)或多個(gè)結(jié)點(diǎn)的情況: //這種情況要先通過plist拿到第二個(gè)結(jié)點(diǎn)的地址: SLTNode* newhead = (*pphead)->next; //使用newhead存儲(chǔ)第二個(gè)結(jié)點(diǎn)地址 //拿到第二個(gè)結(jié)點(diǎn)地址后,再釋放掉頭結(jié)點(diǎn), //實(shí)現(xiàn)頭刪效果: free(*pphead); //這時(shí)要讓第二個(gè)結(jié)點(diǎn)成為新的頭結(jié)點(diǎn): *pphead = newhead; //頭指針指向原本的第二個(gè)結(jié)點(diǎn) } //創(chuàng)建查找函數(shù) -- //查找指定值(x)所在結(jié)點(diǎn)的地址 //接收 鏈表頭指針phead、查找值x SLTNode* SLTFind(SLTNode* phead, SLTDataType x) { //像SLTFind和PrintSList函數(shù)只用頭指針遍歷 //不改變指針本身就不需要用到二級指針 //創(chuàng)建個(gè)變量代替頭指針: SLTNode* cur = phead; //使用while循環(huán)對鏈表進(jìn)行遍歷查找: while (cur != NULL) //只要cur指針指向地址不為空就繼續(xù)循環(huán) //while遍歷時(shí),(cur->next != NULL) 和 (cur != NULL) 的區(qū)別: //(cur->next != NULL):這個(gè)條件最后cur會(huì)是最后結(jié)點(diǎn)的地址 //(cur != NULL):這個(gè)條件最后cur會(huì)是NULL //(cur->next != NULL) 會(huì)比 (cur != NULL) 少一次循環(huán) { //遍歷過程中依次查找各結(jié)點(diǎn)數(shù)據(jù)域數(shù)據(jù)是否與x相同: if (cur->data == x) { //找到了一個(gè)結(jié)點(diǎn)數(shù)據(jù)域數(shù)據(jù)是x,返回該結(jié)點(diǎn)地址 return cur; } //這里如果while循環(huán)的條件是(cur->next != NULL) //最后一個(gè)結(jié)點(diǎn)進(jìn)不來,不能判斷最后結(jié)點(diǎn)數(shù)據(jù)域數(shù)據(jù)是不是x cur = cur->next; //改變循環(huán)條件,指向下個(gè)結(jié)點(diǎn) } //如果能指向到這說明沒找到,返回NULL: return NULL; } //創(chuàng)建指定位置插入函數(shù)1 -- //在鏈表指定位置(pos)之前插入一個(gè)值(x) //接收 鏈表頭指針地址pphead、指定結(jié)點(diǎn)指針pos、插入值x void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) { //因?yàn)閜phead二級指針存儲(chǔ)的是plist指針, //plist指針指向的值可能為空,但指針的地址不能為空 //pphead是plist的地址,正常情況下一定不為空 assert(pphead); //第一種情況:空指針 //先排除空指針的情況: assert(pos); //第二種情況:頭插 if (pos == *pphead) // *pphead == plist //如果pos是pphead即第一個(gè)指針, //則在第一個(gè)指針前插入一個(gè)值,相當(dāng)于頭插 { //直接復(fù)用頭插函數(shù)SLTPustFront: SLTPushFront(pphead, x); //直接傳pphead二級指針過去 } else //第三種情況:非頭插 { //從頭開始找pos結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn): //先獲得頭指針 SLTNode* prev = *pphead; //當(dāng)前結(jié)點(diǎn)的指針域不是指向pos結(jié)點(diǎn)則繼續(xù)循環(huán) while (prev->next != pos) { prev = prev->next; } //此時(shí)prev已指向pos結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn) //為要插入的結(jié)點(diǎn)創(chuàng)建一個(gè)結(jié)點(diǎn)newnode: //插入位置是pos結(jié)點(diǎn)之前,prev結(jié)點(diǎn)之后 SLTNode* newnode = BuySListNode(x); //讓prev結(jié)點(diǎn)指針域指向新插入結(jié)點(diǎn)地址: prev->next = newnode; //newnode結(jié)點(diǎn)指針域指向pos結(jié)點(diǎn): newnode->next = pos; //此時(shí)newnode新結(jié)點(diǎn)就插入完成了 } } //創(chuàng)建指定位置插入函數(shù)2 -- //在鏈表指定位置(pos)之后插入一個(gè)值(x) //接收 指定結(jié)點(diǎn)指針pos、插入值x void SLTInsertAfter(SLTNode* pos, SLTDataType x) { //因?yàn)槭窃趐os結(jié)點(diǎn)后插入一個(gè)值(結(jié)點(diǎn)) //所以不可能會(huì)有頭插的情況,那就不需要頭指針plist //pos指針存儲(chǔ)結(jié)點(diǎn)地址,可能會(huì)接收到空指針 //使用assert斷言pass掉 assert(pos); //先為插入值創(chuàng)建一個(gè)新結(jié)點(diǎn)newnode: SLTNode* newnode = BuySListNode(x); //先讓newnode的指針域next指向后一個(gè)結(jié)點(diǎn): //這里后一個(gè)結(jié)點(diǎn)就是pos結(jié)點(diǎn)指針域里的地址 //因?yàn)槲床迦肭皃os就是指向后一個(gè)地址 newnode->next = pos->next; //再讓pos的指針域next指向newnode: pos->next = newnode; } //創(chuàng)建刪除指定結(jié)點(diǎn)函數(shù)1 -- //在鏈表刪除指定位置(pos)結(jié)點(diǎn) ///接收 鏈表頭指針地址pphead、指定結(jié)點(diǎn)指針pos void SLTErase(SLTNode** pphead, SLTNode* pos) { //因?yàn)閜phead二級指針存儲(chǔ)的是plist指針, //plist指針指向的值可能為空,但指針的地址不能為空 //pphead是plist的地址,正常情況下一定不為空 assert(pphead); //防止要?jiǎng)h的pos結(jié)點(diǎn)指針為空指針 //使用assert斷言: assert(pos); //分兩種情況: // 1.頭刪: if (pos == *pphead) //pos結(jié)點(diǎn) == 頭結(jié)點(diǎn) --> 頭刪 { //直接復(fù)用SLTPopFront頭刪函數(shù): SLTPopFront(pphead); } else // 2.非頭刪: //尾刪不用單獨(dú)分出一種情況,因?yàn)檫€得判斷是不是尾結(jié)點(diǎn) //直接用通用邏輯刪除也可以處理尾刪的情況 { //從頭開始找pos結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn): //先獲得頭指針 SLTNode* prev = *pphead; //當(dāng)前結(jié)點(diǎn)的指針域不是指向pos結(jié)點(diǎn)則繼續(xù)循環(huán) while (prev->next != pos) { prev = prev->next; } //此時(shí)prev已指向pos結(jié)點(diǎn)的前一個(gè)結(jié)點(diǎn) //因?yàn)橐獎(jiǎng)h除pos結(jié)點(diǎn), //所以要讓pos前一個(gè)和后一個(gè)結(jié)點(diǎn)連接起來: prev->next = pos->next; //連接成功后再把pos結(jié)點(diǎn)釋放,實(shí)現(xiàn)刪除效果: free(pos); } } //創(chuàng)建刪除指定結(jié)點(diǎn)函數(shù)2 -- //在鏈表刪除指定位置(pos)之后一個(gè)結(jié)點(diǎn) //接收 指定結(jié)點(diǎn)指針pos void SLTEraseAfter(SLTNode* pos) { //刪除pos結(jié)點(diǎn)后的一個(gè)結(jié)點(diǎn), //那么就不可能出現(xiàn)頭刪的情況, //pos結(jié)點(diǎn)是尾結(jié)點(diǎn)也沒用, //因?yàn)槲步Y(jié)點(diǎn)后就沒有結(jié)點(diǎn)可以刪了 //使用assert斷言處理: assert(pos); //防止接收“空結(jié)點(diǎn)” assert(pos->next); //防止接收尾結(jié)點(diǎn) //將要?jiǎng)h的pos結(jié)點(diǎn)的下個(gè)結(jié)點(diǎn)posNext先保存起來: SLTNode* posNext = pos->next; //再把pos結(jié)點(diǎn)和下下個(gè)結(jié)點(diǎn)連接起來: pos->next = posNext->next; //posNext的指針域next就是pos結(jié)點(diǎn)的下下個(gè)結(jié)點(diǎn)地址 //最后釋放(刪除)posNext結(jié)點(diǎn): free(posNext); } //創(chuàng)建銷毀鏈表函數(shù): void SLTDestroy(SLTNode** pphead) { //因?yàn)殒湵磲尫藕?,還要把頭指針plist給置為空, //要操作到plist,所以要用到二級指針: //plist指向空鏈表,pphead也不能為空: assert(pphead); //創(chuàng)建一個(gè)指針進(jìn)行遍歷: SLTNode* cur = *pphead;//*pphead == plist //進(jìn)行遍歷釋放: while (cur != NULL) { //創(chuàng)建一個(gè)指針保存下個(gè)結(jié)點(diǎn)地址: SLTNode* next = cur->next; //釋放當(dāng)前結(jié)點(diǎn): free(cur); //cur指針移向下一個(gè)結(jié)點(diǎn): cur = next; } //將鏈表頭指針置為空: *pphead = NULL; }
? ? ? ? ? ??
? ? ? ? ? ??
---------------------------------------------------------------------------------------------
test.c?-- 單鏈表測試文件
//鏈表測試文件: #define _CRT_SECURE_NO_WARNINGS 1 /* 鏈表學(xué)習(xí)引入小測試: #include <stdio.h> //重命名一個(gè)單鏈表數(shù)據(jù)類型 typedef int SLTDataType; //SLT -- single link table //創(chuàng)建一個(gè)單鏈表結(jié)點(diǎn)結(jié)構(gòu)體 typedef struct SListNode { //結(jié)點(diǎn)數(shù)據(jù)域: SLTDataType data; //結(jié)點(diǎn)指針域: //因?yàn)槭侵赶騿捂湵斫Y(jié)點(diǎn)結(jié)構(gòu)體的指針, //所以是單鏈表結(jié)點(diǎn)結(jié)構(gòu)體類型的指針 struct SListNode* next; }SLTNode; //將struct SListNode重命名為SLTNode //創(chuàng)建遍歷單鏈表的函數(shù):接收單鏈表結(jié)點(diǎn)的頭指針 void PrintSList(SLTNode* phead) { //phead頭指針指向第一個(gè)結(jié)點(diǎn), //之后還要多次使用phead頭指針, //所以不要改變phead頭指針, //所以可以創(chuàng)建一個(gè)和phead頭指針一樣類型的指針cur代替phead頭指針 SLTNode* cur = phead; //因?yàn)殒湵淼阶詈蠼Y(jié)點(diǎn)的指針域?yàn)镹ULL才結(jié)束 //所以只要cur不為NULL就繼續(xù)遍歷結(jié)點(diǎn)進(jìn)行打?。? while (cur != NULL) { //通過cur當(dāng)前指向的結(jié)點(diǎn)打印cur里數(shù)據(jù)域的內(nèi)容: printf("%d->", cur->data); //通過結(jié)點(diǎn)里指針域指向下一個(gè)結(jié)點(diǎn),方便打印下個(gè)結(jié)點(diǎn)的數(shù)據(jù)域內(nèi)容 cur = cur->next; } //最后提示已指向NULL指針鏈表,遍歷結(jié)束: printf("NULL\n"); } int main() { //創(chuàng)建單鏈表結(jié)點(diǎn): SLTNode* n1 = (SLTNode*)malloc(sizeof(SLTNode)); n1->data = 10; //在該結(jié)點(diǎn)數(shù)據(jù)域存放數(shù)據(jù) SLTNode* n2 = (SLTNode*)malloc(sizeof(SLTNode)); n2->data = 20; //在該結(jié)點(diǎn)數(shù)據(jù)域存放數(shù)據(jù) SLTNode* n3 = (SLTNode*)malloc(sizeof(SLTNode)); n3->data = 30; //在該結(jié)點(diǎn)數(shù)據(jù)域存放數(shù)據(jù) n1->next = n2; //n1的指針域指向結(jié)點(diǎn)n2 n2->next = n3; //n2的指針域指向結(jié)點(diǎn)n3 n3->next = NULL; //n3的指針域指向NULL(結(jié)束) PrintSList(n1); //調(diào)用函數(shù)打印單鏈表 return 0; } */ //包含鏈表頭文件: #include "SList.h" //測試函數(shù)1:測試--PrintSList、BuySListNode函數(shù) void TestList1() { int n; //存放鏈表長度 //打印提示信息: printf("請輸入鏈表的長度:>"); //接收鏈表長度 scanf("%d", &n); //打印提示信息: printf("\n請依次輸入每個(gè)結(jié)點(diǎn)的值:>"); SLTNode* plist = NULL; //鏈表頭指針,一開始鏈表沒數(shù)據(jù)所以為NULL //使用for循環(huán),循環(huán)創(chuàng)建結(jié)點(diǎn)并賦值,形成鏈表: for (int i = 0; i < n; i++) { int val; //存放結(jié)點(diǎn)數(shù)據(jù)域數(shù)據(jù) scanf("%d", &val); //接收結(jié)點(diǎn)數(shù)據(jù)域數(shù)據(jù) //使用BuySListNode函數(shù)創(chuàng)建結(jié)點(diǎn)并給數(shù)據(jù)域賦值: SLTNode* newnode = BuySListNode(val); //頭插: 使用頭插把鏈表連接起來 //把鏈表頭指針plist指向的頭結(jié)點(diǎn)地址賦給新創(chuàng)建結(jié)點(diǎn)的指針域next //這樣新結(jié)點(diǎn)的指針域next就能指向原來的頭結(jié)點(diǎn)地址 newnode->next = plist; //再把新創(chuàng)建結(jié)點(diǎn)的地址賦給頭指針, //這樣頭指針就指向了新創(chuàng)建結(jié)點(diǎn)地址,讓其成為新的頭結(jié)點(diǎn) plist = newnode; } //使用PrintSList函數(shù)打印鏈表 PrintSList(plist); //接收頭指針后打印 //使用SLTPushBack函數(shù)手動(dòng)向鏈表尾部插入數(shù)據(jù)(尾插): SLTPushBack(plist, 10000); //再使用PrintSList函數(shù)打印插入后的鏈表 PrintSList(plist); //plist和phead都是單鏈表頭指針, //但 plist是實(shí)參 phead是形參 //phead 是 plist 的一份臨時(shí)拷貝 } //測試函數(shù)2:測試--SLTPushBack、SLTPushFront函數(shù) void TestList2() { //創(chuàng)建單鏈表頭指針: SLTNode* plist = NULL; //使用尾插隨機(jī)插入幾個(gè)值: //(此時(shí)頭指針指向NULL還沒有開辟空間創(chuàng)建結(jié)點(diǎn)) SLTPushBack(&plist, 1); SLTPushBack(&plist, 2); SLTPushBack(&plist, 3); SLTPushBack(&plist, 4); SLTPushBack(&plist, 5); //使用頭插隨機(jī)插入幾個(gè)值: SLTPushFront(&plist, 10); SLTPushFront(&plist, 20); SLTPushFront(&plist, 30); SLTPushFront(&plist, 40); //使用SLTPrintf函數(shù)打印該鏈表: PrintSList(plist); } //測試函數(shù)3:測試--SLTPopBack(尾刪)函數(shù) void TestList3() { //創(chuàng)建單鏈表頭指針: SLTNode* plist = NULL; //使用尾插隨機(jī)插入幾個(gè)值: //(此時(shí)頭指針指向NULL還沒有開辟空間創(chuàng)建結(jié)點(diǎn)) SLTPushBack(&plist, 1); SLTPushBack(&plist, 2); SLTPushBack(&plist, 3); //使用SLTPrintf函數(shù)打印該鏈表: printf("尾刪前鏈表:\n"); PrintSList(plist); //使用尾刪函數(shù): SLTPopBack(&plist); //使用SLTPrintf函數(shù)打印該鏈表: printf("尾刪后鏈表:\n"); PrintSList(plist); SLTPopBack(&plist); //使用SLTPrintf函數(shù)打印該鏈表: printf("尾刪后鏈表:\n"); PrintSList(plist); SLTPopBack(&plist); //使用SLTPrintf函數(shù)打印該鏈表: printf("尾刪后鏈表:\n"); PrintSList(plist); SLTPopBack(&plist); //使用SLTPrintf函數(shù)打印該鏈表: printf("尾刪后鏈表:\n"); PrintSList(plist); //使用SLTPrintf函數(shù)打印該鏈表: printf("尾刪后鏈表:\n"); PrintSList(plist); } //測試函數(shù)4:測試--SLTPopFront(頭刪)函數(shù) void TestList4() { //創(chuàng)建單鏈表頭指針: SLTNode* plist = NULL; //使用尾插隨機(jī)插入幾個(gè)值: //(此時(shí)頭指針指向NULL還沒有開辟空間創(chuàng)建結(jié)點(diǎn)) SLTPushBack(&plist, 1); SLTPushBack(&plist, 2); SLTPushBack(&plist, 3); //使用SLTPrintf函數(shù)打印該鏈表: printf("頭刪前鏈表:\n"); PrintSList(plist); SLTPopFront(&plist); //使用SLTPrintf函數(shù)打印該鏈表: printf("頭刪后鏈表:\n"); PrintSList(plist); SLTPopFront(&plist); //使用SLTPrintf函數(shù)打印該鏈表: printf("頭刪后鏈表:\n"); PrintSList(plist); SLTPopFront(&plist); //使用SLTPrintf函數(shù)打印該鏈表: printf("頭刪后鏈表:\n"); PrintSList(plist); SLTPopFront(&plist); //使用SLTPrintf函數(shù)打印該鏈表: printf("頭刪后鏈表:\n"); PrintSList(plist); } //測試函數(shù)5:測試 -- SLTFind函數(shù) void TestList5() { //創(chuàng)建單鏈表頭指針: SLTNode* plist = NULL; //使用尾插隨機(jī)插入幾個(gè)值: //(此時(shí)頭指針指向NULL還沒有開辟空間創(chuàng)建結(jié)點(diǎn)) SLTPushBack(&plist, 1); SLTPushBack(&plist, 2); SLTPushBack(&plist, 3); //使用SLTPrintf函數(shù)打印該鏈表: printf("操作前鏈表:\n"); PrintSList(plist); //用SLTFind函數(shù)查找鏈表中數(shù)據(jù)域?yàn)?的結(jié)點(diǎn)的地址 SLTNode* pos = SLTFind(plist, 1); if (pos != NULL) { //找到了可以對該結(jié)點(diǎn)進(jìn)行修改 pos->data *= 10; //所以SLTFind查找函數(shù)可以負(fù)責(zé)查找和修改的操作 } printf("操作后鏈表:\n"); PrintSList(plist); } //測試函數(shù)6:測試 -- SLTInsert函數(shù) void TestList6() { //創(chuàng)建單鏈表頭指針: SLTNode* plist = NULL; //使用尾插隨機(jī)插入幾個(gè)值: //(此時(shí)頭指針指向NULL還沒有開辟空間創(chuàng)建結(jié)點(diǎn)) SLTPushBack(&plist, 1); SLTPushBack(&plist, 2); SLTPushBack(&plist, 3); //使用SLTPrintf函數(shù)打印該鏈表: printf("操作前鏈表:\n"); PrintSList(plist); //用SLTFind函數(shù)查找鏈表中數(shù)據(jù)域?yàn)?的結(jié)點(diǎn)的地址 SLTNode* pos = SLTFind(plist, 2); if (pos != NULL) { int x; //接收要插入的值 scanf("%d", &x); //輸入要插入的值 SLTInsert(&plist, pos, x); //在2前面插入x } printf("操作后鏈表:\n"); PrintSList(plist); } //測試函數(shù)7:測試 -- SLTInsertAfter函數(shù) void TestList7() { //創(chuàng)建單鏈表頭指針: SLTNode* plist = NULL; //使用尾插隨機(jī)插入幾個(gè)值: //(此時(shí)頭指針指向NULL還沒有開辟空間創(chuàng)建結(jié)點(diǎn)) SLTPushBack(&plist, 1); SLTPushBack(&plist, 2); SLTPushBack(&plist, 3); //使用SLTPrintf函數(shù)打印該鏈表: printf("操作前鏈表:\n"); PrintSList(plist); //用SLTFind函數(shù)查找鏈表中數(shù)據(jù)域?yàn)?的結(jié)點(diǎn)d的地址 SLTNode* pos = SLTFind(plist, 2); if (pos != NULL) { int x; //接收要插入的值 scanf("%d", &x); //輸入要插入的值 SLTInsertAfter(pos, x); //在2后面插入x } printf("操作后鏈表:\n"); PrintSList(plist); } //測試函數(shù)8:測試 -- SLTErase函數(shù) void TestList8() { //創(chuàng)建單鏈表頭指針: SLTNode* plist = NULL; //使用尾插隨機(jī)插入幾個(gè)值: //(此時(shí)頭指針指向NULL還沒有開辟空間創(chuàng)建結(jié)點(diǎn)) SLTPushBack(&plist, 1); SLTPushBack(&plist, 2); SLTPushBack(&plist, 3); //使用SLTPrintf函數(shù)打印該鏈表: printf("操作前鏈表:\n"); PrintSList(plist); //用SLTFind函數(shù)查找鏈表中數(shù)據(jù)域?yàn)?的結(jié)點(diǎn)d的地址 SLTNode* pos = SLTFind(plist, 2); if (pos != NULL) { int x; //接收要插入的值 scanf("%d", &x); //輸入要插入的值 SLTErase(&plist, pos); //刪除pos所在結(jié)點(diǎn) //pos結(jié)點(diǎn)指針刪除(釋放)后,要將其置為空: pos = NULL; } printf("操作后鏈表:\n"); PrintSList(plist); } //測試函數(shù)9:測試 -- SLTEraseAfter函數(shù) void TestList9() { //創(chuàng)建單鏈表頭指針: SLTNode* plist = NULL; //使用尾插隨機(jī)插入幾個(gè)值: //(此時(shí)頭指針指向NULL還沒有開辟空間創(chuàng)建結(jié)點(diǎn)) SLTPushBack(&plist, 1); SLTPushBack(&plist, 2); SLTPushBack(&plist, 3); //使用SLTPrintf函數(shù)打印該鏈表: printf("操作前鏈表:\n"); PrintSList(plist); //用SLTFind函數(shù)查找鏈表中數(shù)據(jù)域?yàn)?的結(jié)點(diǎn)d的地址 SLTNode* pos = SLTFind(plist, 2); if (pos != NULL) { int x; //接收要插入的值 scanf("%d", &x); //輸入要插入的值 SLTEraseAfter(pos); //刪除pos結(jié)點(diǎn)的下個(gè)結(jié)點(diǎn) //pos結(jié)點(diǎn)指針刪除(釋放)后,要將其置為空: pos = NULL; } printf("操作后鏈表:\n"); PrintSList(plist); } //測試函數(shù)10:測試 -- SLTDestroy函數(shù) void TestList10() { //創(chuàng)建單鏈表頭指針: SLTNode* plist = NULL; //使用尾插隨機(jī)插入幾個(gè)值: //(此時(shí)頭指針指向NULL還沒有開辟空間創(chuàng)建結(jié)點(diǎn)) SLTPushBack(&plist, 1); SLTPushBack(&plist, 2); SLTPushBack(&plist, 3); //使用SLTPrintf函數(shù)打印該鏈表: printf("操作前鏈表:\n"); PrintSList(plist); SLTDestroy(&plist); } //主函數(shù): int main() { //TestList1(); //TestList2(); //TestList3(); //TestList4(); //TestList5(); //TestList6(); //TestList7(); //TestList8(); //TestList9(); TestList10(); return 0; }
到了這里,關(guān)于【數(shù)據(jù)結(jié)構(gòu)初階】三、 線性表里的鏈表(無頭+單向+非循環(huán)鏈表 -- C語言實(shí)現(xiàn))的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!