一,鏈表的含義
1,火車(chē)引例
? ? ? ??在講解開(kāi)始前,我們先來(lái)看一張圖片:
? ? ? ? 如圖我們可以看到一列火車(chē),它由車(chē)頭和車(chē)廂組成,同時(shí)由鏈條連接,從整個(gè)火車(chē)我們可以看出,前一節(jié)的車(chē)廂尾總有著一個(gè)鏈條,讓它緊密與后一個(gè)車(chē)廂相連。這樣,如果我們找到了前一個(gè)車(chē)廂,那么我們就可以同時(shí)通過(guò)這條鏈子找到下一節(jié)車(chē)廂。而在數(shù)據(jù)結(jié)構(gòu)中,這樣有著“火車(chē)”一樣的結(jié)構(gòu),通過(guò)某種方式將數(shù)據(jù)(車(chē)廂)節(jié)節(jié)相連的線性表,就叫做鏈表。
2,鏈表的概念與圖解
????????鏈表與順序表不同,雖然他們都屬于線性表的一種,但是鏈表的特點(diǎn)卻不同于順序表。鏈表是一種邏輯結(jié)構(gòu)連續(xù),但物理結(jié)構(gòu)不連續(xù)的數(shù)據(jù)儲(chǔ)存結(jié)構(gòu)。它由一個(gè)個(gè)節(jié)點(diǎn)組成,而每個(gè)節(jié)點(diǎn)由它所儲(chǔ)存的數(shù)據(jù)和下一個(gè)結(jié)點(diǎn)的地址組成。由下圖所示:
? ? ? ? 圖中所表示的就是一個(gè)簡(jiǎn)單的鏈表,它們都儲(chǔ)存著各自的數(shù)據(jù),并且儲(chǔ)存著下一個(gè)節(jié)點(diǎn)的地址,而最后一個(gè)節(jié)點(diǎn)的地址被我們置空。由此可見(jiàn),鏈表雖然在物理結(jié)構(gòu)上看似散亂分布,但是它們卻被一條名為“地址”的鏈子連接起來(lái),讓我們可以通過(guò)上一個(gè)節(jié)點(diǎn)而找到下一個(gè)節(jié)點(diǎn)。
? ? ? ? 所以有了上面的詳細(xì)解釋之后,那么我們?nèi)绻攵x一個(gè)鏈表,就易如反掌了。
typedef int SLDataType;//重命名數(shù)據(jù)類(lèi)型,方便以后更改儲(chǔ)存的數(shù)據(jù)類(lèi)型
typedef struct SListNode
{
SLDataType data;//鏈表所儲(chǔ)存的數(shù)據(jù)
struct SListNode* next;//下一個(gè)結(jié)點(diǎn)的地址
}SLD;//將鏈表改名為SLD
二,鏈表操作函數(shù)準(zhǔn)備
? ? ? ? 老樣子,就像我們之前講的順序表操作函數(shù)的實(shí)現(xiàn)一樣,我們都需要生成三個(gè)文件。
? ? ? ? 然后我們需要在SList.h文件中完成我們可能用到的頭文件的包含
#include<stdio.h>
#include<assert.h>//assert斷言用于判斷鏈表是否為空
#include<stdlib.h>//會(huì)用到malloc函數(shù)創(chuàng)建空間
三,鏈表操作函數(shù)的實(shí)現(xiàn)
1,鏈表打印函數(shù)
? ? ? ? 所謂鏈表的打印,就是通過(guò)鏈表的地址為路徑,依次將鏈表所儲(chǔ)存的數(shù)據(jù)打印出來(lái)。這個(gè)鏈表打印實(shí)現(xiàn)函數(shù)我們將于SList.c中進(jìn)行實(shí)現(xiàn)。
//鏈表的打印
void SLDPrint(SLD* phead)
{
while (phead)//phead!=NULL;
{
printf("%d->", phead->data);//打印數(shù)據(jù)
phead = phead->next;//下一個(gè)節(jié)點(diǎn)
}
printf("NULL\n");
}
? ? ? ? 我們將該函數(shù)命名為SLDPrint()。它通過(guò)while循環(huán)進(jìn)行鏈表數(shù)據(jù)的打印,每進(jìn)行一次打印,phead就為被重新賦值為該節(jié)點(diǎn)所儲(chǔ)存的地址(也就是下一個(gè)結(jié)點(diǎn)的地址),當(dāng)phead到達(dá)最后一個(gè)節(jié)點(diǎn)并完成打印時(shí),phead就會(huì)被賦值為NULL,從而跳出while循環(huán),打印結(jié)束。
? ? ? ? 所以我們將該函數(shù)在SList.h中聲明,并于test.c中測(cè)試。
? ? ? ? 測(cè)試函數(shù)代碼如下:
#include"SList.h"
void test()
{
SLD* node1 = (SLD*)malloc(sizeof(SLD));//使用malloc函數(shù)申請(qǐng)一個(gè)結(jié)構(gòu)體的空間
node1->data = 1;//為結(jié)構(gòu)體中的數(shù)據(jù)賦值
SLD* node2 = (SLD*)malloc(sizeof(SLD));
node2->data = 2;
SLD* node3 = (SLD*)malloc(sizeof(SLD));
node3->data = 3;
SLD* node4 = (SLD*)malloc(sizeof(SLD));
node4->data = 4;
node1->next = node2;//將鏈表一個(gè)一個(gè)的使用地址連起來(lái)
node2->next = node3;
node3->next = node4;
node4->next = NULL;//最后一個(gè)節(jié)點(diǎn)所儲(chǔ)存的地址賦值為NULL
SLD* plist = node1;//給plist賦值為node1的地址
SLDPrint(plist);//開(kāi)始打?。?}
? ? ? ? 于是我們代碼走起來(lái),結(jié)果如下:
? ? ? ? 鏈表打印函數(shù)實(shí)現(xiàn)成功!
2,鏈表尾插函數(shù)
? ? ? ? 碼如其名,該函數(shù)的作用就是在鏈表的最后插入一個(gè)新的節(jié)點(diǎn)(這個(gè)函數(shù)是我們剛接觸鏈表數(shù)據(jù)改變的第一個(gè)函數(shù),可能較難理解,可一旦理解這個(gè)函數(shù),后面的函數(shù)對(duì)你來(lái)說(shuō)就不在話下了)。話不多說(shuō),我們先上圖解釋?zhuān)?/span>
? ? ? ? 難點(diǎn)1:由圖可知,要進(jìn)行鏈表的尾插操作,我們就需要首先創(chuàng)造出來(lái)一個(gè)新的節(jié)點(diǎn),來(lái)儲(chǔ)存新數(shù)據(jù)x的值,同時(shí)因?yàn)槭俏膊?,所以我們同時(shí)還應(yīng)該將新的尾節(jié)點(diǎn)所儲(chǔ)存的地址初始化為NULL。所以我們應(yīng)該于SList.c函數(shù)中寫(xiě)一個(gè)創(chuàng)建新節(jié)點(diǎn)的函數(shù)SLDCreatNode()函數(shù)?。
//創(chuàng)建一個(gè)新結(jié)點(diǎn)
SLD* SLDCreatNode(SLDataType x)
{
SLD* newnode = (SLD*)malloc(sizeof(SLD));//malloc函數(shù)創(chuàng)建一個(gè)新節(jié)點(diǎn)
if (newnode==NULL)//判斷是否內(nèi)存申請(qǐng)成功
{
perror("malloc fail!");
exit(1);//非正常退出
}
newnode->data = x;//為節(jié)點(diǎn)data賦值
newnode->next = NULL;//將節(jié)點(diǎn)的next值置空
return newnode;//返回節(jié)點(diǎn)地址
}
? ? ? ? 該函數(shù)的返回值就是新創(chuàng)建節(jié)點(diǎn)結(jié)構(gòu)體的地址。函數(shù)中我們使用malloc函數(shù)申請(qǐng)一個(gè)節(jié)點(diǎn)結(jié)構(gòu)體的空間,然后將該節(jié)點(diǎn)所儲(chǔ)存的數(shù)據(jù)初始化為x,后將該節(jié)點(diǎn)所儲(chǔ)存的地址置空。
? ? ? ? 難點(diǎn)2:既然我們想要對(duì)鏈表進(jìn)行尾插,那么我們總得找到當(dāng)前鏈表最后一個(gè)節(jié)點(diǎn)吧。所以我們將設(shè)計(jì)一個(gè)SLDptail()函數(shù),來(lái)找到鏈表的最后一個(gè)節(jié)點(diǎn):
//尋找尾節(jié)點(diǎn)
SLD* SLDptail(SLD* phead)/傳給該函數(shù)首節(jié)點(diǎn)的地址
{
assert(phead);//防止空鏈表
SLD* ptail = phead;
while (ptail->next != NULL)//利用最后一個(gè)節(jié)點(diǎn)所儲(chǔ)存的地址為NULL的特點(diǎn)
{
ptail = ptail->next;//ptail依次往后走
}
return ptail;//返回最后一個(gè)節(jié)點(diǎn)的地址
}
? ? ? ? 這樣,通過(guò)while循環(huán),我們就找到了鏈表尾節(jié)點(diǎn)的地址。?
????????難點(diǎn)3:這也是本函數(shù)比較燒腦的地方。讓我們仔細(xì)想一想,如果我們?nèi)缦露x函數(shù)形參,會(huì)發(fā)生什么問(wèn)題?
void SLDPushBack(SLD* pphead,SLDataType x)
? ? ? ? 如果我們這樣設(shè)計(jì)函數(shù),那么該函數(shù)在使用時(shí)進(jìn)行一份臨時(shí)拷貝,編譯器則會(huì)在另一個(gè)完全不同的空間中進(jìn)行鏈表的尾插,而你原來(lái)的鏈表不會(huì)有任何變化。所以我們需要使用二級(jí)指針,進(jìn)行節(jié)點(diǎn)地址的地址的傳址調(diào)用,從而完成對(duì)原來(lái)鏈表內(nèi)容的改變。所以我們?nèi)缦露x:
void SLDPushBack(SLD** pphead, SLDataType x)
? ? ? ? 所以各個(gè)符號(hào)所表示的含義如下:
? ? ? ? 在這些難點(diǎn)攻克之后,其實(shí)想要寫(xiě)出鏈表尾插函數(shù)就十分簡(jiǎn)單了,代碼如下:
//鏈表的尾插
void SLDPushBack(SLD** pphead, SLDataType x)
{
assert(pphead);
SLD* newnode = SLDCreatNode(x);//創(chuàng)建一個(gè)新節(jié)點(diǎn)
//討論情況:空鏈表與非空鏈表
if (*pphead == NULL)//空鏈表
{
*pphead = newnode;
}
else//非空鏈表
{
//找到尾節(jié)點(diǎn)
SLD* ptail = SLDptail(*pphead);
ptail->next = newnode;
}
}
? ? ? ? 我們首先創(chuàng)造一個(gè)新節(jié)點(diǎn)newnode。如果鏈表為空,那么我們就直接將newnode的地址設(shè)為鏈表首節(jié)點(diǎn)的地址,節(jié)點(diǎn)插入成功;如果鏈表不為空,我們就先使用SLDptail()函數(shù)找到尾節(jié)點(diǎn),再將尾節(jié)點(diǎn)所儲(chǔ)存的地址賦值為newnode的地址,這樣就尾插成功了!
? ? ? ? 我們將此函數(shù)聲明后,于test.c中測(cè)試,代碼如下:
#include"SList.h"
void test()
{
SLD* plist = NULL;
SLDPushBack(&plist, 1);//進(jìn)行尾插
SLDPushBack(&plist, 2);
SLDPushBack(&plist, 3);
SLDPushBack(&plist, 4);
SLDPrint(plist);//對(duì)鏈表進(jìn)行打印
}
? ? ? ? 代碼走起來(lái),結(jié)果如圖:
? ? ? ? 鏈表尾插函數(shù)實(shí)現(xiàn)成功!
3,鏈表頭插函數(shù)
? ? ? ? 頭插頭插,就是在鏈表的第一個(gè)節(jié)點(diǎn)之前再插入一個(gè)新節(jié)點(diǎn),讓新節(jié)點(diǎn)成為鏈表的首節(jié)點(diǎn)。而我想說(shuō)的是,這個(gè)函數(shù)十分簡(jiǎn)單?。?!
? ? ? ? 如圖所示,我們只需要將這個(gè)新創(chuàng)建的節(jié)點(diǎn)所儲(chǔ)存的地址賦值為原來(lái)鏈表首節(jié)點(diǎn)的地址就可以了,完全不需要像順序表一樣進(jìn)行什么數(shù)據(jù)的移位操作。所以于SList.c中,該函數(shù)代碼如下:
//鏈表的頭插
void SLDPushFront(SLD** pphead, SLDataType x)
{
assert(pphead);
SLD* newnode = SLDCreatNode(x);//創(chuàng)建一個(gè)新節(jié)點(diǎn)
newnode->next = *pphead;//將新節(jié)點(diǎn)所儲(chǔ)存的地址賦值為原來(lái)鏈表首節(jié)點(diǎn)的地址
*pphead = newnode;//將newnode視為新的第一個(gè)節(jié)點(diǎn)
}
? ? ? ? 根據(jù)之后我們的分析可得,這個(gè)函數(shù)對(duì)于空鏈表也同樣適用。那讓我們聲明該函數(shù)之后在test.c中測(cè)試一下吧!
#include"SList.h"
void test()
{
SLD* plist = NULL;
SLDPushBack(&plist, 1);//進(jìn)行尾插
SLDPushBack(&plist, 2);
SLDPushBack(&plist, 3);
SLDPushBack(&plist, 4);
SLDPushFront(&plist, 5);//進(jìn)行頭插
SLDPrint(plist);//對(duì)鏈表進(jìn)行打印
}
? ? ? ? 代碼我們走起來(lái),結(jié)果如下:
? ? ? ? 鏈表頭插函數(shù)實(shí)現(xiàn)成功!
4,鏈表尾刪函數(shù)
? ? ? ? 鏈表的尾刪函數(shù),無(wú)非就主要分為兩種操作:1,將鏈表最后的一個(gè)節(jié)點(diǎn)空間釋放掉,并且將最后一個(gè)節(jié)點(diǎn)所保存的地址置為空;2,將原本倒數(shù)第二個(gè)節(jié)點(diǎn)所保存的地址置為空。我們畫(huà)圖解釋更清楚。
? ? ? ? 同時(shí)我們也應(yīng)該創(chuàng)建一個(gè)pre結(jié)構(gòu)體指針,用于保存倒數(shù)第二個(gè)節(jié)點(diǎn)的地址。但我們也不能忘了討論當(dāng)鏈表只有一個(gè)節(jié)點(diǎn)的情況。若鏈表只有一個(gè)節(jié)點(diǎn),那么則滿足表達(dá)式((*pphead)->next==NULL),所以我們直接將第一個(gè)節(jié)點(diǎn)釋放掉并且將第一個(gè)節(jié)點(diǎn)的地址置空。那么完整的代碼如下:
//鏈表的尾刪
void SLDDeleteBack(SLD** pphead)
{
assert(pphead);
assert(*pphead);
//只有一個(gè)節(jié)點(diǎn)時(shí)
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else//有多個(gè)節(jié)點(diǎn)時(shí)
{
//先找到尾部節(jié)點(diǎn)并且保存尾節(jié)點(diǎn)之前的一個(gè)節(jié)點(diǎn)的地址
SLD* ptail = *pphead;
SLD* pre = *pphead;
while (ptail->next)
{
pre = ptail;
ptail = ptail->next;
}
pre->next = NULL;//尾節(jié)點(diǎn)前的一個(gè)節(jié)點(diǎn)的next置空
free(ptail);//釋放尾節(jié)點(diǎn)空間
ptail = NULL;//防止野指針的出現(xiàn)
}
}
? ? ? ? 先聲明后測(cè)試:
#include"SList.h"
void test()
{
SLD* plist = NULL;
SLDPushBack(&plist, 1);//進(jìn)行尾插
SLDPushBack(&plist, 2);
SLDPushBack(&plist, 3);
SLDPushBack(&plist, 4);
SLDPushFront(&plist, 5);//進(jìn)行頭插
SLDDeleteBack(&plist);//進(jìn)行尾刪
SLDPrint(plist);//對(duì)鏈表進(jìn)行打印
}
? ? ? ? 結(jié)果如下圖所示:
? ? ? ? 鏈表尾刪函數(shù)實(shí)現(xiàn)成功!?
5,鏈表頭刪函數(shù)
? ? ? ? 鏈表的頭刪函數(shù)與鏈表的頭插函數(shù)的難度十分相似,都非常簡(jiǎn)單!我們所需要做的就是要釋放掉第一個(gè)節(jié)點(diǎn)的空間并且將第二個(gè)節(jié)點(diǎn)的地址賦值為第一個(gè)節(jié)點(diǎn)的地址。但是我們需要先注意,不可以先釋放頭節(jié)點(diǎn),因?yàn)檫@樣可能導(dǎo)致我們找不到第二個(gè)節(jié)點(diǎn)。我們畫(huà)圖看一下吧:
所以代碼如下:
//鏈表的頭刪
void SLDDeleteFront(SLD** pphead)
{
//鏈表不為空
assert(pphead && *pphead);
SLD* next = (*pphead)->next;//將頭節(jié)點(diǎn)所儲(chǔ)存的地址命名為next
free(*pphead);//釋放掉頭節(jié)點(diǎn)的空間
*pphead = next;//將第二個(gè)節(jié)點(diǎn)變?yōu)轭^節(jié)點(diǎn)
}
? ? ? ? 該代碼中,我們首先將next賦值為第一個(gè)節(jié)點(diǎn)所儲(chǔ)存的地址(也就是第二個(gè)結(jié)點(diǎn)的地址),這樣,在保證第二個(gè)節(jié)點(diǎn)的地址可以被找到的情況下,我們釋放掉第一個(gè)節(jié)點(diǎn)的空間。然后我們?cè)賹⒈硎镜谝粋€(gè)節(jié)點(diǎn)的地址的*pphead賦值為next,標(biāo)志著第二個(gè)節(jié)點(diǎn)正式成為第一個(gè)節(jié)點(diǎn)。
? ? ? ? 所以我們先聲明后測(cè)試,測(cè)試代碼如下:
#include"SList.h"
void test()
{
SLD* plist = NULL;
SLDPushBack(&plist, 1);//進(jìn)行尾插
SLDPushBack(&plist, 2);
SLDPushBack(&plist, 3);
SLDPushBack(&plist, 4);
SLDDeleteFront(&plist);//對(duì)鏈表進(jìn)行頭刪
SLDPrint(plist);//對(duì)鏈表進(jìn)行打印
}
? ? ? ? 代碼走起來(lái),結(jié)果如下圖所示:
? ? ? ? 鏈表頭刪函數(shù)實(shí)現(xiàn)成功!
6,鏈表查找函數(shù)
? ? ? ? 鏈表的查找函數(shù),作用就是找出鏈表中是否儲(chǔ)存著某個(gè)數(shù)據(jù),如果該數(shù)據(jù)存在,那么則返回儲(chǔ)存這該數(shù)據(jù)的節(jié)點(diǎn)的地址;如果不存在,那么則返回NULL。該函數(shù)十分簡(jiǎn)單,我們直接上代碼!
//鏈表的查找
SLD* SLDFind(SLD* phead, SLDataType x)
{
SLD* pcur = phead;//將第一個(gè)節(jié)點(diǎn)的地址賦值給pcur
while (pcur->next)
{
if (pcur->data == x)
{
return pcur;//找到了
}
pcur = pcur->next;
}
//遍歷了一遍鏈表都沒(méi)找到X,則返回NULL
return NULL;
}
? ? ? ? 經(jīng)過(guò)我們的測(cè)試,這個(gè)函數(shù)同樣也對(duì)空鏈表適用。該函數(shù)創(chuàng)建了一個(gè)變量pcur賦值為首節(jié)點(diǎn)的地址(為了防止phead被改變),然后使用while循環(huán)遍歷鏈表,如果找到了則返回該節(jié)點(diǎn)地址(pcur),若沒(méi)找到則返回NULL。
? ? ? ? 老樣子,我們先聲明再測(cè)試:
#include"SList.h"
void test()
{
SLD* plist = NULL;
SLDPushBack(&plist, 1);//進(jìn)行尾插
SLDPushBack(&plist, 2);
SLDPushBack(&plist, 3);
SLDPushBack(&plist, 4);
SLD* find = SLDFind(plist, 3);//尋找鏈表中數(shù)據(jù)為3的節(jié)點(diǎn)
if (find != NULL)
{
printf("找到了");
}
else
{
printf("沒(méi)有找到");
}
}
? ? ? ? 代碼一走!結(jié)果如下圖所示:
? ? ? ? 鏈表查找函數(shù)實(shí)現(xiàn)成功!
7,鏈表指定位置之前插入數(shù)據(jù)函數(shù)
? ? ? ? 該函數(shù)的作用是在我們所指定的節(jié)點(diǎn)位置(SLD* pos)之前插入一個(gè)新節(jié)點(diǎn)。它需要我們正確地運(yùn)用節(jié)點(diǎn)創(chuàng)建函數(shù),讓我們畫(huà)圖解釋?zhuān)?/p>
? ? ? ? 如圖所示,我們需要除了pphead以外的兩個(gè)新的變量pre(用來(lái)表示指定位置之前的節(jié)點(diǎn)地址),pos(用于表示指定位置的節(jié)點(diǎn))。然后我們?cè)賱?chuàng)造一個(gè)新節(jié)點(diǎn),將pre->next賦值為新節(jié)點(diǎn)的地址,后將新節(jié)點(diǎn)所儲(chǔ)存的地址賦值為pos,這樣,一個(gè)新節(jié)點(diǎn)就成功插入了。當(dāng)然,我們不能忘記討論只有一個(gè)節(jié)點(diǎn)的情況,這也非常簡(jiǎn)單,只有一個(gè)節(jié)點(diǎn)的話,不就是頭插了嗎,我們直接調(diào)用頭插函數(shù)就行了唄!所以該函數(shù)的代碼如下:
//鏈表的指定位置之前插入數(shù)據(jù)
void SLDInsert(SLD** pphead, SLD* pos, SLDataType x)
{
assert(pphead && *pphead);
assert(pos);//指定位置不可以為空
SLD* newnode = SLDCreatNode(x);//創(chuàng)建一個(gè)新節(jié)點(diǎn)
if (pos == *pphead)
{
SLDPushFront(pphead, x);//調(diào)用頭插函數(shù)
}
else
{
SLD* pre = *pphead;
while (pre->next != pos)
{
pre = pre->next;
}
//新節(jié)點(diǎn)的首尾相連
pre->next = newnode;
newnode->next = pos;
}
}
? ? ? ? 我們先聲明再測(cè)試,測(cè)試代碼如下:
#include"SList.h"
void test()
{
SLD* plist = NULL;
SLDPushBack(&plist, 1);//進(jìn)行尾插
SLDPushBack(&plist, 2);
SLDPushBack(&plist, 3);
SLDPushBack(&plist, 4);
SLD* find = SLDFind(plist, 3);
SLDInsert(&plist, find, 100);//插入一個(gè)數(shù)據(jù)值為100的新節(jié)點(diǎn)
SLDPrint(plist);//對(duì)鏈表進(jìn)行打印
}
? ? ? ? 那咱們代碼走起來(lái),結(jié)果如圖所示:
? ? ? ? 鏈表指定位置之前插入數(shù)據(jù)函數(shù)實(shí)現(xiàn)成功!?
8,鏈表指定位置之后插入數(shù)據(jù)函數(shù)
? ? ? ? 對(duì)于這個(gè)函數(shù),所用到的參數(shù)就比前一個(gè)函數(shù)更少,這是因?yàn)樵谇耙粋€(gè)函數(shù),如果我們想要找到pos之前的節(jié)點(diǎn)的地址,我們需要使用while函數(shù)從頭遍歷一遍鏈表。而這個(gè)函數(shù)不用,因?yàn)槲覀兛梢灾苯油ㄟ^(guò)pos找到pos之后節(jié)點(diǎn)的地址。就像下圖所示:
? ? ? ? 所以我們只需要?jiǎng)?chuàng)造一個(gè)新節(jié)點(diǎn)newnode,然后將newnode所儲(chǔ)存的地址賦值為pos->next,最后再將pos->next賦值為newnode就可以了,是不是非常簡(jiǎn)單!所以我們的代碼如下:
//鏈表的指定位置之后插入數(shù)據(jù)
void SLDInsertAfter(SLD* pos, SLDataType x)
{
assert(pos);//pos不可以為空指針
SLD* newnode = SLDCreatNode(x);
//首尾相連
newnode->next = pos->next;
pos->next = newnode;
}
? ? ? ? 所以我們先聲明再測(cè)試:
#include"SList.h"
void test()
{
SLD* plist = NULL;
SLDPushBack(&plist, 1);//進(jìn)行尾插
SLDPushBack(&plist, 2);
SLDPushBack(&plist, 3);
SLDPushBack(&plist, 4);
SLD* find = SLDFind(plist, 3);
SLDInsertAfter( find, 100);//插入一個(gè)數(shù)據(jù)值為100的新節(jié)點(diǎn)
SLDPrint(plist);//對(duì)鏈表進(jìn)行打印
}
? ? ? ? 咱們代碼走起來(lái),結(jié)果如下圖所示:
? ? ? ? 鏈表指定位置之后插入數(shù)據(jù)函數(shù)實(shí)現(xiàn)成功!
9,鏈表刪除指定位置節(jié)點(diǎn)函數(shù)
? ? ? ? 該函數(shù)的作用是刪除指定位置的節(jié)點(diǎn),并且將其前后的節(jié)點(diǎn)重新連接起來(lái),但是在實(shí)現(xiàn)過(guò)程中,我們需要注意刪除的節(jié)點(diǎn)是否為頭節(jié)點(diǎn)等特殊情況。如下圖所示:
????????我們先看代碼,再進(jìn)行細(xì)講吧!
//鏈表的指定位置刪除節(jié)點(diǎn)
void SLDDelete(SLD** pphead, SLD* pos)
{
assert(pphead && *pphead);
assert(pos);//pos不可以為空指針
if (pos == *pphead)//pos為頭節(jié)點(diǎn)
{
SLDDeleteFront(pphead);//直接調(diào)用頭刪函數(shù)
}
else
{
SLD* pre = *pphead;
while (pre->next != pos)//遍歷鏈表,當(dāng)pre為pos前一個(gè)結(jié)點(diǎn)時(shí)則退出循環(huán)
{
pre = pre->next;
}
pre->next = pos->next;//連接pre與pos之后的節(jié)點(diǎn)
free(pos);
pos = NULL;//防止野指針的出現(xiàn)
}
}
? ? ? ? ?在該函數(shù)中,我們還是先用assert斷言保證鏈表和pos的非空,然后我們?cè)龠M(jìn)行pos是否為頭節(jié)點(diǎn)的分類(lèi)討論。
????????當(dāng)pos為頭節(jié)點(diǎn)時(shí),那此時(shí)這個(gè)函數(shù)就相當(dāng)于頭刪,那我們直接調(diào)用我們先前實(shí)現(xiàn)的頭刪函數(shù)就行了。
????????當(dāng)pos不為頭節(jié)點(diǎn)時(shí),那么我們就需要定義一個(gè)pre結(jié)構(gòu)體指針,用于保存pos前一個(gè)節(jié)點(diǎn)的地址,然后我們使用while循環(huán)遍歷鏈表,并且在pre->next!=pos條件為假時(shí)停下,那么此時(shí)pre正好表示pos之前一個(gè)節(jié)點(diǎn)的地址。最后我們?cè)賹?strong>pre->next賦值為pos->next以完成新節(jié)點(diǎn)的首尾相連,釋放pos并且防止pos成為野指針。這樣,鏈表刪除指定位置節(jié)點(diǎn)函數(shù)就完成了!
? ? ? ? 讓我們先聲明后測(cè)試:
#include"SList.h"
void test()
{
SLD* plist = NULL;
SLDPushBack(&plist, 1);//進(jìn)行尾插
SLDPushBack(&plist, 2);
SLDPushBack(&plist, 3);
SLDPushBack(&plist, 4);
SLD* find = SLDFind(plist, 3);
SLDDelete(&plist, find);//刪除數(shù)據(jù)值為3的節(jié)點(diǎn)
SLDPrint(plist);//對(duì)鏈表進(jìn)行打印
}
? ? ? ? 我們代碼走起來(lái),那么所得到的結(jié)果如下:
? ? ? ? 鏈表刪除指定位置節(jié)點(diǎn)函數(shù)實(shí)現(xiàn)成功!
10,鏈表刪除指定位置之后節(jié)點(diǎn)函數(shù)
? ? ? ? 這是我們所接觸鏈表操作函數(shù)的最后一個(gè)函數(shù)了!它可以刪除掉鏈表指定位置之后的節(jié)點(diǎn),讓我們直接看圖說(shuō)話!
? ? ? ? 如圖所示,pos之后的那個(gè)節(jié)點(diǎn)才是我們想要?jiǎng)h除的,所以我們將那個(gè)節(jié)點(diǎn)命名為del,所以我們需要做的就是先將pos與pos->next->next連接起來(lái),然后釋放掉del就行了,十分簡(jiǎn)單!
? ? ? ? 那么代碼如下圖所示:
void SLDDeleteAfter(SLD** pphead, SLD* pos)
{
assert(*pphead && pphead);
assert(pos && pos->next);//pos和pos的下一個(gè)節(jié)點(diǎn)都不可以為空
SLD* del = pos->next;//del為要被刪除的節(jié)點(diǎn)
pos->next = del->next;//首尾相連
free(del);
del = NULL;//防止野指針
}
? ? ? ? 所以我們先聲明后測(cè)試,測(cè)試代碼如下:
#include"SList.h"
void test()
{
SLD* plist = NULL;
SLDPushBack(&plist, 1);//進(jìn)行尾插
SLDPushBack(&plist, 2);
SLDPushBack(&plist, 3);
SLDPushBack(&plist, 4);
SLD* find = SLDFind(plist, 3);
SLDDeleteAfter(&plist, find);//刪除數(shù)據(jù)值為3的節(jié)點(diǎn)之后的節(jié)點(diǎn)
SLDPrint(plist);//對(duì)鏈表進(jìn)行打印
}
? ? ? ? 結(jié)果如下圖所示:
? ? ? ? 鏈表刪除指定位置之后節(jié)點(diǎn)函數(shù)實(shí)現(xiàn)成功!
四,結(jié)語(yǔ)
? ? ? ? 現(xiàn)在我已經(jīng)從一個(gè)C語(yǔ)言小白變成了一個(gè)略懂一點(diǎn)數(shù)據(jù)結(jié)構(gòu)的C語(yǔ)言小白了,感覺(jué)在學(xué)習(xí)編程的路上逐漸走上坡路了,也慢慢有了對(duì)編程的興趣了,這篇九千三百多字的文章也算是一個(gè)很不錯(cuò)的里程碑了。
? ? ? ? 本人才疏學(xué)淺,希望我的這篇文章能給你們帶來(lái)幫助,也非常希望可以得到給位的支持,加油吧,好好學(xué)習(xí),未來(lái)可期??!文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-856448.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-856448.html
到了這里,關(guān)于C語(yǔ)言鏈表的含義與鏈表數(shù)據(jù)操作代碼詳解!的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!