欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++超詳細(xì)講解單鏈表的實(shí)現(xiàn)

 更新時(shí)間:2022年03月30日 16:59:46   作者:雪芙花  
單鏈表是后面要學(xué)的雙鏈表以及循環(huán)鏈表的基礎(chǔ),要想繼續(xù)深入了解數(shù)據(jù)結(jié)構(gòu)以及C++,我們就要奠定好這塊基石!接下來就和我一起學(xué)習(xí)吧

單鏈表的實(shí)現(xiàn)(從入門到熟練)

概念和結(jié)構(gòu)

概念:鏈表是一種物理存儲結(jié)構(gòu)上非連續(xù)、非順序的存儲結(jié)構(gòu)

數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈 接次序?qū)崿F(xiàn)的

圖示:

image-20220318195224112

注意:

鏈表結(jié)構(gòu)在邏輯上為連續(xù)的,但是物理上(內(nèi)存中)不一定連續(xù)

鏈表節(jié)點(diǎn)都是在堆上申請出來的,申請空間按一定策略分配

結(jié)構(gòu)種類

鏈表具有多種結(jié)構(gòu):單向\雙向,帶頭\不帶頭,循環(huán)\非循環(huán)

實(shí)際上最常用的是:無頭單向非循環(huán)鏈表,帶頭雙向循環(huán)鏈表

鏈表的實(shí)現(xiàn)

注意:這里實(shí)現(xiàn)的是無頭單向非循環(huán)鏈表

增刪查改接口

// 動態(tài)申請一個(gè)節(jié)點(diǎn)
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
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 單鏈表刪除pos位置之后的值
void SListEraseAfter(SListNode* pos);

節(jié)點(diǎn)結(jié)構(gòu)體創(chuàng)建

typedef int SLTDateType;
typedef struct SListNode
{
 SLTDateType data;
 struct SListNode* next; 
}SListNode;

節(jié)點(diǎn)開辟

對于鏈表來說,每需要空間就需要進(jìn)行開辟,這里我們將之封裝成一個(gè)函數(shù),便于后續(xù)調(diào)用直接使用(開辟的同時(shí)進(jìn)行賦值)

參考代碼:

//鏈表節(jié)點(diǎn)開辟
SLTNode* BuySListNode(SLTDateType x)
{
	//動態(tài)分配一個(gè)節(jié)點(diǎn)
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		//分配失敗則打印錯誤信息并結(jié)束進(jìn)程
		perror("newnode fail:");
		exit(-1);
	}
	//成功則進(jìn)行賦值
	newnode->data = x;
	newnode->next = NULL;
	//返回節(jié)點(diǎn)地址,以便后續(xù)操作
	return newnode;
}

數(shù)據(jù)打印

注意:

1.對于不帶頭的鏈表來說,打印數(shù)據(jù)不需要修改鏈表首節(jié)點(diǎn)地址(故只要傳鏈表指針)

2.用循環(huán)遍歷鏈表,每打印數(shù)據(jù),需要指向下一個(gè)節(jié)點(diǎn)

3.依靠尾節(jié)點(diǎn)的址域?yàn)镹ULL來結(jié)束循環(huán)

代碼:

//鏈表數(shù)據(jù)打印
void SListPrint(SLTNode* phead)//一級指針接收一級指針(打印不需改變指針本身內(nèi)容)
{
	//創(chuàng)建一個(gè)尋址指針
	SLTNode* cur = phead;
	while (cur!=NULL)//后續(xù)還有節(jié)點(diǎn)
	{
		//打印數(shù)據(jù)并指向下一個(gè)節(jié)點(diǎn)
		printf("%d->", cur->data);
		cur = cur->next;
	}
	//最后打印空指針
	printf("NULL\n");
	return;
}

鏈表尾插數(shù)據(jù)

  • 要尾插數(shù)據(jù)則需要遍歷找到鏈表的尾節(jié)點(diǎn)
  • 對于不帶頭鏈表,尾插數(shù)據(jù)也可能是插在鏈表首元素的地址(當(dāng)鏈表為空),需要修改鏈表指針的內(nèi)容(故需要傳入鏈表指針的地址)
  • 插入數(shù)據(jù)要開辟節(jié)點(diǎn)

代碼:

//鏈表尾插數(shù)據(jù)
void SListPushBack(SLTNode** pphead, SLTDateType x)//二級指針接收一級指針(尾插存在需改變鏈表指針本身內(nèi)容)
{
	//避免傳入錯誤(直接報(bào)錯便于找到錯誤位置)
	assert(pphead);
	//接收新節(jié)點(diǎn)的地址
	SLTNode* newnode=BuySListNode(x);
	//頭節(jié)點(diǎn)為空
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾節(jié)點(diǎn)
		SLTNode* tail = *pphead;//創(chuàng)建尋址節(jié)點(diǎn)
		//兩個(gè)及以上節(jié)點(diǎn)的情況
		while (tail->next != NULL)
		{
			//指向下一個(gè)節(jié)點(diǎn)
			tail = tail->next;	
		}
		//找到了
		tail->next = newnode;
	}
	return;
}

注意代碼中的assert的作用:

  • 正確傳入鏈表指針的地址是不會為空的
  • 但是對于非正常傳入鏈表指針(不是鏈表指針的地址)且此時(shí)鏈表指針為空則會發(fā)生報(bào)錯(assert報(bào)錯會告訴錯誤位置),告訴程序員應(yīng)該傳入鏈表指針的地址

頭刪

注意:

  • 刪除前需要保存當(dāng)前節(jié)點(diǎn)的址域(即保存下個(gè)節(jié)點(diǎn)的空間地址,以防丟失)
  • 前刪數(shù)據(jù)即刪除當(dāng)前鏈表首節(jié)點(diǎn),即需修改鏈表指針的內(nèi)容(故需傳入鏈表指針的地址)
  • 刪除后修改鏈表指針內(nèi)容,指向新的首節(jié)點(diǎn)
  • 如果鏈表為空時(shí)無法刪除(保存下個(gè)節(jié)點(diǎn)地址會造成非法訪問)

代碼:

//鏈表前刪數(shù)據(jù)
void SListPopFront(SLTNode** pphead)
{
	//避免傳入錯誤(直接報(bào)錯便于找到錯誤位置)
	assert(pphead);
	//鏈表為空的情況
	if (*pphead == NULL)
	{
		return;
	}
	//創(chuàng)建指針保存第二個(gè)節(jié)點(diǎn)地址
	SLTNode* next = (*pphead)->next;
	//釋放當(dāng)前頭結(jié)點(diǎn)空間
	free(*pphead);
	//更新頭結(jié)點(diǎn)數(shù)據(jù)
	*pphead = next;
	return;
}

鏈表數(shù)據(jù)查找

注意:

  • 查找時(shí)用循環(huán)遍歷鏈表
  • 對于查找數(shù)據(jù)不用修改鏈表指針的內(nèi)容,故只需傳入鏈表指針就行了
  • 查找到時(shí)則返回節(jié)點(diǎn)地址,否則返回NULL

代碼:

//鏈表數(shù)據(jù)查找
SLTNode* SListFind(SLTNode* phead, SLTDateType x)
{
	//創(chuàng)建尋址指針
	SLTNode* cur = phead;
	while (cur!=NULL)
	{
		if (cur->data == x)//找到數(shù)據(jù)符合節(jié)點(diǎn)
		{
			return cur;//返回節(jié)點(diǎn)地址(好處:以便后續(xù)再尋找或修改)
		}
		else
		{
			//找下一個(gè)節(jié)點(diǎn)
			cur = cur->next;
		}
	}
	//未找到數(shù)據(jù)符合節(jié)點(diǎn)
	return NULL;
}

鏈表pos位置前插數(shù)據(jù)

注意:

  • 想要pos位置前插數(shù)據(jù),不僅需要找到pos位置,還需要記錄pos的前一個(gè)節(jié)點(diǎn)位置
  • 傳入的pos為NULL則報(bào)錯
  • 進(jìn)行修改前節(jié)點(diǎn)的址域成新節(jié)點(diǎn),并讓新節(jié)點(diǎn)的址域修改成pos,這才是一個(gè)成功的pos位置前插數(shù)據(jù)
  • 循環(huán)遍歷鏈表查找pos位置,沒有找到pos位置則什么也不干

代碼:

//鏈表pos位置往前插入(較難)(還有在pos位置之后插入,簡單點(diǎn))
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
	//避免傳入錯誤(直接報(bào)錯便于找到錯誤位置)
	assert(pphead);
	assert(pos);
	SLTNode* newnode = BuySListNode(x);
	//首結(jié)點(diǎn)符合的情況
	if (*pphead == pos)
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
	else
	{
		//創(chuàng)建尋址指針
		SLTNode* cur = *pphead;
		while (cur !=NULL)
		{
			if (cur->next!= pos)
			{
				cur = cur->next;//找到下一節(jié)點(diǎn)
			}
			else // 找到對應(yīng)空間
			{
				cur->next = newnode;
				newnode->next = pos;
				return;//結(jié)束尋找(否則會一直插入,造成死循環(huán))
			}
		}
	}
	//未找到則什么也不干
	return;
}

鏈表pos位置后插數(shù)據(jù)

注意:

  • 后插則不用關(guān)注是否為首節(jié)點(diǎn)
  • 也不用找到遍歷找到前節(jié)點(diǎn)的位置
  • 后插則先將新節(jié)點(diǎn)址域改成pos后節(jié)點(diǎn)地址再將pos的址域改成新節(jié)點(diǎn)地址

ps:一定要注意修改鏈接節(jié)點(diǎn)址域的先后,避免地址的丟失

代碼:

//鏈表pos位置往后插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
	SLTNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
	return;
}

鏈表pos位置數(shù)據(jù)刪除

注意:

  • 考慮刪除首節(jié)點(diǎn)的情況,可能修改鏈表指針的內(nèi)容,故需要傳入鏈表指針的地址
  • 對于刪除節(jié)點(diǎn),需要先保存pos位置下一個(gè)節(jié)點(diǎn)地址,將pos位置釋放,再將pos位置前節(jié)點(diǎn)的址域改成pos位置后節(jié)點(diǎn)的地址,這才是成功的刪除pos位置節(jié)點(diǎn)
  • 循環(huán)找pos位置,沒找到則什么也不干

參考代碼:

//鏈表pos位置刪除
void SListErase(SLTNode** pphead, SLTNode* pos)
{
	//避免傳入錯誤(直接報(bào)錯便于找到錯誤位置)
	assert(pphead);
	assert(pos);
	//頭結(jié)點(diǎn)符合的情況
	if (*pphead == pos)
	{
		*pphead = (*pphead)->next;
		free(pos);
	}
	else
	{
		//創(chuàng)建尋址指針
		SLTNode* cur = *pphead;
		while (cur != NULL)
		{
			if (cur->next != pos)
			{
				cur = cur->next;//找到下一節(jié)點(diǎn)
			}
			else // 找到對應(yīng)空間
			{
				SLTNode* next = cur->next->next;
				free(cur->next);
				cur->next = next;
				return;//結(jié)束尋找
			}
		}
	}
	//未找到則什么也不干
	return;
}

鏈表節(jié)點(diǎn)釋放

注意:

  • 對于動態(tài)開辟的內(nèi)存空間,在使用后一定要記得的進(jìn)行釋放(避免造成內(nèi)存泄漏)
  • 因?yàn)殒湵砉?jié)點(diǎn)是一個(gè)個(gè)開辟的,同樣的釋放也需要一個(gè)個(gè)進(jìn)行釋放
  • 循環(huán)遍歷釋放當(dāng)前節(jié)點(diǎn)前需保存后一個(gè)節(jié)點(diǎn)的地址,避免地址丟失無法釋放
  • 釋放完后,還需將鏈表指針給置空(避免使用野指針)

參考代碼:

//鏈表節(jié)點(diǎn)釋放
void SListDestory(SLTNode** pphead)
{
	//避免傳入錯誤(直接報(bào)錯便于找到錯誤位置)
	assert(pphead);
	//遍歷釋放
	SLTNode* cur = *pphead;
	while (cur)
	{
		//保存下一個(gè)地址
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	//置空
	*pphead = NULL;
	return;
}

鏈表與順序表區(qū)別

鏈表的優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 按需申請空間(空間使用合理)
  • 插入效率高(不用像順序表樣挪動數(shù)據(jù))

缺點(diǎn)

  • 不支持隨機(jī)訪問(無法用下標(biāo)直接訪問)

順序表的優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 支持隨機(jī)訪問 (有些算法需要結(jié)構(gòu)支持隨機(jī)訪問:二分查找,快排等)

缺點(diǎn)

  • 擴(kuò)容空間有消耗(空間碎片化以及空間浪費(fèi))
  • 插入數(shù)據(jù)需要挪動數(shù)據(jù)有消耗

到此這篇關(guān)于C++超詳細(xì)講解單鏈表的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++ 單鏈表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談C++11中=delete的巧妙用法

    淺談C++11中=delete的巧妙用法

    本文主要介紹了C++11中=delete的巧妙用法,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • C++淺析STL?迭代器?容器的使用

    C++淺析STL?迭代器?容器的使用

    這篇文章主要介紹了C++?STL、迭代器、容器,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-07-07
  • C語言實(shí)現(xiàn)掃雷游戲源代碼

    C語言實(shí)現(xiàn)掃雷游戲源代碼

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)掃雷游戲源代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-03-03
  • 帶你從頭學(xué)習(xí)C++的封裝

    帶你從頭學(xué)習(xí)C++的封裝

    這篇文章主要為大家從頭學(xué)習(xí)了C++的封裝,使用數(shù)據(jù)庫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C語言進(jìn)階可變參數(shù)列表

    C語言進(jìn)階可變參數(shù)列表

    這篇文章主要為大家介紹了C語言進(jìn)階可變參數(shù)列表的示例詳解有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-02-02
  • Qt如何通過pos()獲取坐標(biāo)信息

    Qt如何通過pos()獲取坐標(biāo)信息

    這篇文章主要給大家介紹了關(guān)于Qt如何通過pos()獲取坐標(biāo)信息的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用qt具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-01-01
  • C語言圍圈報(bào)數(shù)題目代碼實(shí)現(xiàn)

    C語言圍圈報(bào)數(shù)題目代碼實(shí)現(xiàn)

    大家好,本篇文章主要講的是C語言圍圈報(bào)數(shù)題目代碼實(shí)現(xiàn),感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • C++ 數(shù)據(jù)結(jié)構(gòu)線性表-數(shù)組實(shí)現(xiàn)

    C++ 數(shù)據(jù)結(jié)構(gòu)線性表-數(shù)組實(shí)現(xiàn)

    這篇文章主要介紹了C++ 數(shù)據(jù)結(jié)構(gòu)線性表-數(shù)組實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • C++鏈表節(jié)點(diǎn)的添加和刪除介紹

    C++鏈表節(jié)點(diǎn)的添加和刪除介紹

    大家好,本篇文章主要講的是C++鏈表節(jié)點(diǎn)的添加和刪除介紹,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • C基礎(chǔ) redis緩存訪問詳解

    C基礎(chǔ) redis緩存訪問詳解

    下面小編就為大家?guī)硪黄狢基礎(chǔ) redis緩存訪問詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-06-06

最新評論