国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【數(shù)據(jù)結(jié)構(gòu)】單鏈表的增刪查改(C實(shí)現(xiàn))

這篇具有很好參考價值的文章主要介紹了【數(shù)據(jù)結(jié)構(gòu)】單鏈表的增刪查改(C實(shí)現(xiàn))。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

前言

引入鏈表:順序表的問題及思考與討論

優(yōu)勢

  1. 可通過 下標(biāo)i (數(shù)據(jù)連續(xù)(物理空間連續(xù))) 便捷查詢查找順序表中的信息,也會在后面的 排序算法堆算法 中盡顯身手

問題

  1. 在頭部/中間的插入與刪除需要挪動數(shù)據(jù),時間復(fù)雜度為O(N),效率低;

  2. 增容需要申請新空間,可能會拷貝數(shù)據(jù),釋放舊空間,會有不小的消耗;

  3. 增容一般是呈 1.5倍2倍的增長,勢必會有一定的空間浪費(fèi)。例如當(dāng)前容量為100,滿了以后增容到 200,如果我們再繼續(xù)插入了5個數(shù)據(jù),后面沒有數(shù)據(jù)插入了,那么會浪費(fèi)95個數(shù)據(jù)空間;

正因順序表的這些不足,我們設(shè)計(jì)出了鏈表。



一、鏈表

1、鏈表的概念及結(jié)構(gòu)

1.1 概念
  • 鏈表 是一種 物理存儲結(jié)構(gòu)非連續(xù)、非順序 的存儲結(jié)構(gòu),
  • 數(shù)據(jù)元素的邏輯順序是通過 鏈表中的 指針鏈接 次序 實(shí)現(xiàn)的 。

[注: 所謂的邏輯結(jié)構(gòu)指的是數(shù)據(jù)在邏輯上是如何存儲的,這是由人們主觀想象出來的;而物理結(jié)構(gòu)則是數(shù)據(jù)在物理內(nèi)存中實(shí)際存儲的方式,不隨人們的主觀意志而改變。]


帶大家感受一下鏈表;

鏈表就如同圖中的火車:
plist = 火車頭,指向第一個節(jié)點(diǎn)
每個節(jié)點(diǎn) = 每一節(jié)的火車車廂
每一節(jié)點(diǎn)中存儲的下一節(jié)點(diǎn)的指針 =火車車廂之間的鏈接
【數(shù)據(jù)結(jié)構(gòu)】單鏈表的增刪查改(C實(shí)現(xiàn)),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,c#,鏈表
【數(shù)據(jù)結(jié)構(gòu)】單鏈表的增刪查改(C實(shí)現(xiàn)),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,c#,鏈表

實(shí)際上鏈表的每一個節(jié)點(diǎn)都是在堆區(qū)上隨機(jī)申請的,前一個節(jié)點(diǎn)的地址可能比后一個節(jié)點(diǎn)大,也可能比后一個節(jié)點(diǎn)小,二者之前其實(shí)并沒有物理結(jié)構(gòu)上的關(guān)系。

鏈表的結(jié)構(gòu)也可以這樣

【數(shù)據(jù)結(jié)構(gòu)】單鏈表的增刪查改(C實(shí)現(xiàn)),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,c#,鏈表


★☆★ 總結(jié)


注意:

  1. 從上圖可看出,鏈?zhǔn)浇Y(jié)構(gòu)在邏輯上是連續(xù)的,但是在物理上不一定連續(xù)
  2. 現(xiàn)實(shí)中的結(jié)點(diǎn)一般都是從上申請出來的 (malloc
  3. 從堆上申請的空間,是按照一定的策路來分配的,兩次申清的空同可能連續(xù),也可能不連續(xù)


    鏈表和順序表的 不同之處 在于:
    順序表不僅要求邏輯結(jié)構(gòu)上連續(xù),還要求物理結(jié)構(gòu)上也連續(xù);而鏈表 只要求邏輯結(jié)構(gòu)上連續(xù)物理結(jié)構(gòu)上可以不連續(xù);正因?yàn)殒湵硎怯?指針 相連的特性。


2、鏈表的分類

  1. 單向/雙向

    雙向鏈表對比單向鏈表來說,其結(jié)構(gòu)體中會多一個結(jié)構(gòu)體指針變量,用來指向前一個節(jié)點(diǎn)的地址。
    【數(shù)據(jù)結(jié)構(gòu)】單鏈表的增刪查改(C實(shí)現(xiàn)),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,c#,鏈表

  2. 帶頭/不帶頭

    帶頭(哨兵位)最開始的時候會有一個節(jié)點(diǎn),這個節(jié)點(diǎn)不用來存儲數(shù)據(jù),僅僅作為鏈表的頭部使用,還是一個節(jié)點(diǎn)都沒有。
    【數(shù)據(jù)結(jié)構(gòu)】單鏈表的增刪查改(C實(shí)現(xiàn)),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,c#,鏈表

  3. 循環(huán)/不循環(huán)

    非循環(huán)鏈表的最后一個節(jié)點(diǎn)的next指向NULL,而循環(huán)鏈表的最后一個節(jié)點(diǎn)的next指向鏈表的第一個節(jié)點(diǎn)。
    【數(shù)據(jù)結(jié)構(gòu)】單鏈表的增刪查改(C實(shí)現(xiàn)),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,c#,鏈表

3. 最常用的兩種鏈表

雖然鏈表有這么多中結(jié)構(gòu),但是我們實(shí)際中最常用還是以下兩種結(jié)構(gòu):無頭單向非循環(huán)鏈表雙向帶頭循環(huán)鏈表。

(1)無頭單向非循環(huán)鏈表

【數(shù)據(jù)結(jié)構(gòu)】單鏈表的增刪查改(C實(shí)現(xiàn)),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,c#,鏈表

結(jié)構(gòu)簡單,一般不會單獨(dú)用來存數(shù)據(jù),實(shí)際中更多是作為其他數(shù)據(jù)結(jié)構(gòu)的子結(jié)構(gòu),如哈希桶、圖的鄰接表等等;另外這種結(jié)構(gòu)在筆試面試中出現(xiàn)很多;其實(shí)如果不做特殊聲明,一般情況下無頭單向非循環(huán)鏈表指的就是我們的單鏈表


(2)帶頭雙向循環(huán)鏈表

【數(shù)據(jù)結(jié)構(gòu)】單鏈表的增刪查改(C實(shí)現(xiàn)),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,c#,鏈表

結(jié)構(gòu)最復(fù)雜,一般用于單獨(dú)存儲數(shù)據(jù);實(shí)際中我們使用的鏈表數(shù)據(jù)結(jié)構(gòu),都是帶頭雙向循環(huán)鏈表;另外它雖然結(jié)構(gòu)復(fù)雜,但是使用代碼實(shí)現(xiàn)后會有發(fā)現(xiàn)結(jié)構(gòu)會帶來多優(yōu)勢,所以反而是鏈表中使用起來最簡單的。



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

由于單鏈表是其他結(jié)構(gòu)鏈表學(xué)習(xí)的基礎(chǔ),且經(jīng)常被用做其他數(shù)據(jù)結(jié)構(gòu)的子結(jié)構(gòu),在筆試題中也最常被考到,所以下面我們用C語言來手動實(shí)現(xiàn)一個單鏈表,以此來加深我們對單鏈表的理解。

(一)創(chuàng)建文件

  1. slist.h (單鏈表的類型定義、接口函數(shù)聲明、引用的頭文件)
  2. slist.c (單鏈表接口函數(shù)的實(shí)現(xiàn))
  3. test.c (主函數(shù)、測試順序表各個接口功能)

【數(shù)據(jù)結(jié)構(gòu)】單鏈表的增刪查改(C實(shí)現(xiàn)),數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu),c語言,c#,鏈表



(二)slist.h

1、結(jié)構(gòu)體類型聲明

//類型聲明
typedef int SLTDateType;      //數(shù)據(jù)類型重命名
typedef struct SListNode {    //結(jié)構(gòu)體類型聲明
	SLTDateType data;
	struct SListNode* next;   //結(jié)構(gòu)體指針-存放下一節(jié)點(diǎn)(結(jié)構(gòu)體)的地址

}SListNode;


(三)slist.c

注解都標(biāo)注在代碼旁邊,方便大家邊看代碼邊理解

2、動態(tài)創(chuàng)建新節(jié)點(diǎn)

//動態(tài)創(chuàng)建新節(jié)點(diǎn)
SListNode* BuySListNode(SLTDateType x) {

	SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
	assert(malloc);   //避免malloc失敗返回的空指針NULL,并及時報(bào)錯

	newNode->data = x;
	newNode->next = NULL;
	return newNode;
}

3、在頭部插入數(shù)據(jù)

//單鏈表的頭插
void SListPushFront(SListNode** pphead, SLTDateType x) {

	assert(pphead);      //確保傳過來的不是空指針

	SListNode* newNode = BuySListNode(x);      //指針變量中存放的是指針(地址)

	//鏈表為空  直接存放newNode的地址 在 解引用頭指針處得到的內(nèi)容處  //圖解
	if (*pphead == NULL) {
		*pphead = newNode;
		return;

	}

	//鏈表不為空                         //圖解
	SListNode* temp = *pphead;    //先將頭指針中 所指向的節(jié)點(diǎn)地址,先暫時保存
	*pphead = newNode;            //把新節(jié)點(diǎn)地址存入頭指針中,形成新連接
	newNode->next = temp;         //在把暫存的原后鏈接的地址,放入到新節(jié)點(diǎn)newNode中的 結(jié)構(gòu)體指針中,
	                              //再形成新的后鏈接

}

4、在尾部插入數(shù)據(jù)

//單鏈表的尾插
void SListPushBack(SListNode** pphead, SLTDateType x) {

	assert(pphead);   //確保傳過來的不是空指針

	SListNode* newNode = BuySListNode(x);   //指針變量中存放的是指針(地址)


	//鏈表為空  直接存放newNode的地址 在 解引用頭指針處得到的內(nèi)容處  //圖解
	if (*pphead == NULL) {
		*pphead = newNode;
		return;

	}


	//鏈表不為空 我們需要找到鏈尾
	SListNode* tail = *pphead;      //創(chuàng)建個結(jié)構(gòu)體指針遍歷鏈表,
	while (tail->next != NULL) {
		tail = tail->next;
	}                               //找到最后為空的結(jié)構(gòu)體,將其newNode地址插入該末尾結(jié)構(gòu)體
	tail->next = newNode;

}

5、查找數(shù)據(jù)

// 查找數(shù)據(jù)——單鏈表查找
SListNode* SListFind(SListNode* pphead, SListDateType x) {
	assert(pphead);
	SListNode* cur = pphead;

	while (cur != NULL) {
		if (cur->data == x)
			return cur;

		cur = cur->next;
	}
	return NULL;

}

6、在pos位置前插入數(shù)據(jù)

//在pos位置前插入數(shù)據(jù)
void SListInsert(SListNode** pphead, SListNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(pos);
	if (pos == *pphead)  //如果pos等于*pphead,相當(dāng)于頭插
	{
		SListPushFront(pphead, x);
		return;
	}

	SListNode* newNode = BuySLTNode(x);
	//找到pos位置的前一個節(jié)點(diǎn)
	SListNode* prev = *pphead;
	while (prev->next != pos)
	{
		assert(prev);  //如果prev為空循環(huán)還沒停止,說明在鏈表中找不到pos,直接報(bào)錯
		prev = prev->next;
	}
	prev->next = newNode;
	newNode->next = pos;
}

7、在pos位置后插入數(shù)據(jù)

思考:為什么不在pos位置之前插入?

由于單鏈表在某一節(jié)點(diǎn)的前面插入數(shù)據(jù)時需要從頭遍歷尋找該節(jié)點(diǎn)的前一個節(jié)點(diǎn), 導(dǎo)致時間復(fù)雜度為O(N),
所以人們?yōu)榱颂岣邌捂湵淼男剩瑸閱捂湵韱为?dú)設(shè)計(jì)了在pos位置后插入數(shù)據(jù)的函數(shù);除了單鏈表,其他數(shù)據(jù)結(jié)構(gòu)插入數(shù)據(jù)都是在前面插入。

// 單鏈表在pos位置之后插入x

void SListInsertAfter(SListNode* pos, SLTDateType x) {
	assert(pos);

	SListNode* next = pos->next;
	SListNode* newNode = BuySListNode(x);
	newNode->next = next;
	pos->next = newNode;

}

8、在頭部刪除數(shù)據(jù)

// 單鏈表頭刪
void SListPopFront(SListNode** pphead) {
	SListNode* temp = *pphead;

	if (temp == NULL) {                  //先判斷鏈表中是否還有數(shù)據(jù)可被刪除
		printf("鏈表中無數(shù)據(jù)可刪除\n");
		return;
	}

	*pphead = temp->next;                 //圖解

	free(temp);                           //用個臨時變量 記住要free掉的節(jié)點(diǎn)的地址
	temp = NULL;                          //記住后就可以放心大膽的將其后一個節(jié)點(diǎn)的地址傳給頭指針中,
	                                      //這樣就不怕要free掉的節(jié)點(diǎn)的地址被覆蓋而找不到該地址 

}

9、在尾部刪除數(shù)據(jù)

// 單鏈表的尾刪
void SListPopBack(SListNode** pphead) {
	SListNode* temp = *pphead;      //由于后面還會用到頭指針中所存儲的下一節(jié)點(diǎn)的地址,
	                   //所以干脆直接解引用拿出得到頭指針中所存儲的下一節(jié)點(diǎn)的地址,并用SListNode*保存  
	
	                   //(而不是SListNode** temp存儲pphead的地址,
	                   //后面還要解引用*temp才能得到pphead中存儲的(頭指針中所存儲的下一節(jié)點(diǎn)的地址,
	                   //*temp雖說也是指針,但 `*temp->` 這樣的格式編譯器是不允許的不成立的))
	                   // =>所以還是直接解引用的好

	if (temp == NULL) {                  //先判斷鏈表中是否還有數(shù)據(jù)可被刪除
		printf("鏈表中無數(shù)據(jù)可刪除\n");
		return;
	}


	while (temp->next->next != NULL) {      //要刪除最后一個節(jié)點(diǎn),當(dāng)然要找到的是它前一個節(jié)點(diǎn)的地址 
	      // [ 因?yàn)榍耙粋€節(jié)點(diǎn)的結(jié)構(gòu)體中存有最后一個節(jié)點(diǎn)結(jié)構(gòu)體的地址 ](沒有前一個節(jié)點(diǎn)的地址根本無法找到)
		
		temp = temp->next;                //當(dāng)然沒有前一個節(jié)點(diǎn),最后那個節(jié)點(diǎn)遍歷到temp->next=NULL,
		    //temp也能遍歷到最后那個節(jié)點(diǎn)的地址,并將其free掉,但前一個地址的next也要重新設(shè)為空指針NULL,
		    //(就算再遍歷一遍鏈表,結(jié)尾不為NULL,也再也找不到現(xiàn)在的最后一個節(jié)點(diǎn)了
		    //(因?yàn)闊o法讓遍歷碰到現(xiàn)在的新最后一個節(jié)點(diǎn)停下),
		    //那么也將無法刪除最后一個節(jié)點(diǎn)后 的新的最后一個節(jié)點(diǎn)中的next置為NULL)

	}

	SListNode* temp1 = temp->next;        //free掉最后一個節(jié)點(diǎn)
	free(temp1);
	temp1 = NULL;                         //記得要置回NULL空指針,避免變成野指針,很危險(xiǎn)

	temp->next = NULL;                    //將前一個節(jié)點(diǎn)的指針置為NULL

}

10、刪除pos位置前的數(shù)據(jù)

//刪除pos位置前的數(shù)據(jù)
void SListErase(SListNode** pphead, SListNode* pos)
{
	assert(pphead && pos);
	assert(*pphead);  //當(dāng)鏈表為空時,刪除元素報(bào)錯
	if (pos == *pphead)  //pos等于*pphead時相當(dāng)于頭刪
	{
		SListPopFront(pphead);
		return;
	}
	//找到pos的前一個節(jié)點(diǎn)
	SListNode* prev = *pphead;
	while (prev->next != pos)
	{
		assert(prev);  //如果prev為空循環(huán)還沒停止,說明在鏈表中找不到pos,直接報(bào)錯
		prev = prev->next;
	}
	SListNode* tmp = pos->next;
	prev->next = tmp;
	free(pos);
	pos = NULL;
}


11、刪除pos位置后的數(shù)據(jù)

思考:為什么不刪除pos位置?

和在pos位置后插入數(shù)據(jù)一樣,為了提高效率,人們也設(shè)計(jì)了一個在pos位置后刪除數(shù)據(jù)的函數(shù)。文章來源地址http://www.zghlxwxcb.cn/news/detail-742852.html

// 單鏈表刪除pos位置之后的值
void SListEraseAfter(SListNode* pos) {
	assert(pos);  //確保指針不為空
	if (pos->next != NULL) {
		SListNode* next = pos->next->next;
		SListNode* temp = pos->next;
		free(temp);
		temp = NULL;
		pos->next = next;
	}

}

12、修改pos位置處的數(shù)據(jù)

//修改pos位置處的數(shù)據(jù)
void SListModify(SLTNode* phead, SLTNode* pos, SLTDataType x)
{
	assert(phead && pos);
	SListNode* cur = phead;
	while (cur != pos)
	{
		assert(cur);  //如果cur為空循環(huán)還沒停止,說明在鏈表中找不到pos,直接報(bào)錯
		cur = cur->next;
	}
	cur->data = x;
}


13、打印鏈表中的數(shù)據(jù)

//打印鏈表 遞歸實(shí)現(xiàn)
void SListPrint(SListNode* pList) {

	printf("%d ", pList->data);
	SListPrint(pList->next);

}

14、銷毀鏈表

// 單鏈表的銷毀
void SListDestroy(SListNode* pphead) {  //pphead的內(nèi)存空間也要銷毀
	assert(pphead);
	SListNode* cur = pphead;
	while (cur != NULL) {
		SListNode* temp = cur->next;  //用個臨時變量先存著后一個節(jié)點(diǎn)的地址,
		                              //以免free掉現(xiàn)在的地址后,找不到后面的地址了
		free(cur);
		cur = temp;   //再重新找回來

	}

}


三、完整代碼

1、SList.h

#pragma once   //防止頭文件重復(fù)包含

//頭文件
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>


//類型聲明
typedef int SLTDateType;      //數(shù)據(jù)類型重命名
typedef struct SListNode {    //結(jié)構(gòu)體類型聲明
	SLTDateType data;
	struct SListNode* next;   //結(jié)構(gòu)體指針-存放下一節(jié)點(diǎn)(結(jié)構(gòu)體)的地址

}SListNode;


//函數(shù)聲明
//創(chuàng)建新建節(jié)點(diǎn)
SListNode* BuySLTNode(SLTDateType x);

//在頭部插入數(shù)據(jù)
void SListPushFront(SListNode** pphead, SLTDateType x);

//銷毀鏈表
void SListDestory(SListNode** pphead);

//在尾部插入數(shù)據(jù)
void SListPushBack(SListNode** pphead, SLTDateType x);

//查找數(shù)據(jù)
SListNode* SListFind(SListNode* phead, SLTDateType x);

//在pos之前插入數(shù)據(jù)
void SListInsert(SListNode** pphead, SListNode* pos, SLTDateType x);

//在pos之后插入數(shù)據(jù)
void SListInsertAfter(SListNode** pphead, SListNode* pos, SLTDateType x);

//打印鏈表
void SListPrint(SListNode* phead);

//在頭部刪除數(shù)據(jù)
void SListPopFront(SListNode** pphead);

//在尾部刪除數(shù)據(jù)
void SListPopBack(SListNode** pphead);

//刪除pos位置處的數(shù)據(jù)
void SListErase(SListNode** pphead, SListNode* pos);

//刪除pos位置后的數(shù)據(jù)
void SListEraseAfter(SListNode** pphead, SListNode* pos);

//修改pos位置處的函數(shù)
void SListModify(SListNode* phead, SListNode* pos, SLTDateType x);


2、SList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"slist.h"



//動態(tài)創(chuàng)建新節(jié)點(diǎn)
SListNode* BuySListNode(SLTDateType x) {

	SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
	assert(malloc);   //避免malloc失敗返回的空指針NULL,并及時報(bào)錯

	newNode->data = x;
	newNode->next = NULL;
	return newNode;
}



// 單鏈表的頭插
void SListPushFront(SListNode** pphead, SLTDateType x) {

	assert(pphead);   //確保傳過來的不是空指針

	SListNode* newNode = BuySListNode(x);   //指針變量中存放的是指針(地址)

	//鏈表為空  直接存放newNode的地址 在 解引用頭指針處得到的內(nèi)容處  //圖解
	if (*pphead == NULL) {
		*pphead = newNode;
		return;

	}

	//鏈表不為空                         //圖解
	SListNode* temp = *pphead;    //先將頭指針中 所指向的節(jié)點(diǎn)地址,先暫時保存
	*pphead = newNode;            //把新節(jié)點(diǎn)地址存入頭指針中,形成新連接
	newNode->next = temp;         //在把暫存的原后鏈接的地址,放入到新節(jié)點(diǎn)newNode中的 結(jié)構(gòu)體指針中,
	                              //再形成新的后鏈接

}



//單鏈表的尾插
void SListPushBack(SListNode** pphead, SLTDateType x) {

	assert(pphead);   //確保傳過來的不是空指針

	SListNode* newNode = BuySListNode(x);   //指針變量中存放的是指針(地址)


	//鏈表為空  直接存放newNode的地址 在 解引用頭指針處得到的內(nèi)容處  //圖解
	if (*pphead == NULL) {
		*pphead = newNode;
		return;

	}


	//鏈表不為空 我們需要找到鏈尾
	SListNode* tail = *pphead;    //創(chuàng)建個結(jié)構(gòu)體指針遍歷鏈表,

	while (tail->next != NULL) {
		tail = tail->next;        //找到最后為空的結(jié)構(gòu)體,將其newNode地址插入該末尾結(jié)構(gòu)體
	}
	tail->next = newNode;

}



// 查找數(shù)據(jù)——單鏈表查找
SListNode* SListFind(SListNode* pphead, SLTDateType x) {
	assert(pphead);
	SListNode* cur = pphead;

	while (cur != NULL) {
		if (cur->data == x)
			return cur;

		cur = cur->next;
	}
	return NULL;

}



// 單鏈表在pos位置之后插入x
// 分析思考為什么不在pos位置之前插入?    
//   
// 由于單鏈表在某一節(jié)點(diǎn)的前面插入數(shù)據(jù)時需要從頭遍歷尋找該節(jié)點(diǎn)的前一個節(jié)點(diǎn),
// 導(dǎo)致時間復(fù)雜度為O(N),所以人們?yōu)榱颂岣邌捂湵淼男剩?/span>
// 為單鏈表單獨(dú)設(shè)計(jì)了在pos位置后插入數(shù)據(jù)的函數(shù);
// 除了單鏈表,其他數(shù)據(jù)結(jié)構(gòu)插入數(shù)據(jù)都是在前面插入。
void SListInsertAfter(SListNode* pos, SLTDateType x) {
	assert(pos);

	SListNode* next = pos->next;
	SListNode* newNode = BuySListNode(x);
	newNode->next = next;
	pos->next = newNode;

}


//在pos位置前插入數(shù)據(jù)
void SListInsert(SListNode** pphead, SListNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(pos);
	if (pos == *pphead)  //如果pos等于*pphead,相當(dāng)于頭插
	{
		SListPushFront(pphead, x);
		return;
	}

	SListNode* newNode = BuySLTNode(x);
	//找到pos位置的前一個節(jié)點(diǎn)
	SListNode* prev = *pphead;
	while (prev->next != pos)
	{
		assert(prev);  //如果prev為空循環(huán)還沒停止,說明在鏈表中找不到pos,直接報(bào)錯
		prev = prev->next;
	}
	prev->next = newNode;
	newNode->next = pos;
}



// 單鏈表刪除pos位置之后的值
// 分析思考為什么不刪除pos位置?               //和在pos位置后插入數(shù)據(jù)一樣,為了提高效率,人們也設(shè)計(jì)了一個在pos位置后刪除數(shù)據(jù)的函數(shù)。
void SListEraseAfter(SListNode* pos) {
	assert(pos);  //確保指針不為空
	if (pos->next != NULL) {
		SListNode* next = pos->next->next;
		SListNode* temp = pos->next;
		free(temp);
		temp = NULL;
		pos->next = next;
	}

}



//修改pos位置處的數(shù)據(jù)
void SListModify(SListNode* phead, SListNode* pos, SLTDateType x)
{
	assert(phead && pos);
	SListNode* cur = phead;
	while (cur != pos)
	{
		assert(cur);  //如果cur為空循環(huán)還沒停止,說明在鏈表中找不到pos,直接報(bào)錯
		cur = cur->next;
	}
	cur->data = x;
}



// 單鏈表頭刪
void SListPopFront(SListNode** pphead) {
	SListNode* temp = *pphead;

	if (temp == NULL) {                  //先判斷鏈表中是否還有數(shù)據(jù)可被刪除
		printf("鏈表中無數(shù)據(jù)可刪除\n");
		return;
	}

	*pphead = temp->next;                 //圖解

	free(temp);                           //用個臨時變量 記住要free掉的節(jié)點(diǎn)的地址
	temp = NULL;                          //記住后就可以放心大膽的將其后一個節(jié)點(diǎn)的地址傳給頭指針中,
	                                      //這樣就不怕要free掉的節(jié)點(diǎn)的地址被覆蓋而找不到該地址 

}




// 單鏈表的尾刪
void SListPopBack(SListNode** pphead) {
	SListNode* temp = *pphead;      //由于后面還會用到頭指針中所存儲的下一節(jié)點(diǎn)的地址,
	                   //所以干脆直接解引用拿出得到頭指針中所存儲的下一節(jié)點(diǎn)的地址,并用SListNode*保存  
	
	                   //(而不是SListNode** temp存儲pphead的地址,
	                   //后面還要解引用*temp才能得到pphead中存儲的(頭指針中所存儲的下一節(jié)點(diǎn)的地址,
	                   //*temp雖說也是指針,但 `*temp->` 這樣的格式編譯器是不允許的不成立的))
	                   // =>所以還是直接解引用的好

	if (temp == NULL) {                  //先判斷鏈表中是否還有數(shù)據(jù)可被刪除
		printf("鏈表中無數(shù)據(jù)可刪除\n");
		return;
	}


	while (temp->next->next != NULL) {      //要刪除最后一個節(jié)點(diǎn),當(dāng)然要找到的是它前一個節(jié)點(diǎn)的地址 
	      // [ 因?yàn)榍耙粋€節(jié)點(diǎn)的結(jié)構(gòu)體中存有最后一個節(jié)點(diǎn)結(jié)構(gòu)體的地址 ](沒有前一個節(jié)點(diǎn)的地址根本無法找到)
		
		temp = temp->next;                //當(dāng)然沒有前一個節(jié)點(diǎn),最后那個節(jié)點(diǎn)遍歷到temp->next=NULL,
		    //temp也能遍歷到最后那個節(jié)點(diǎn)的地址,并將其free掉,但前一個地址的next也要重新設(shè)為空指針NULL,
		    //(就算再遍歷一遍鏈表,結(jié)尾不為NULL,也再也找不到現(xiàn)在的最后一個節(jié)點(diǎn)了
		    //(因?yàn)闊o法讓遍歷碰到現(xiàn)在的新最后一個節(jié)點(diǎn)停下),
		    //那么也將無法刪除最后一個節(jié)點(diǎn)后 的新的最后一個節(jié)點(diǎn)中的next置為NULL)

	}

	SListNode* temp1 = temp->next;        //free掉最后一個節(jié)點(diǎn)
	free(temp1);
	temp1 = NULL;                         //記得要置回NULL空指針,避免變成野指針,很危險(xiǎn)

	temp->next = NULL;                    //將前一個節(jié)點(diǎn)的指針置為NULL

}





//打印鏈表 遞歸實(shí)現(xiàn)
void SListPrint(SListNode* pList) {

	printf("%d ", pList->data);
	SListPrint(pList->next);

}



// 單鏈表的銷毀
void SListDestroy(SListNode* pphead) {  //pphead的內(nèi)存空間也要銷毀
	assert(pphead);
	SListNode* cur = pphead;
	while (cur != NULL) {
		SListNode* temp = cur->next;  //用個臨時變量先存著后一個節(jié)點(diǎn)的地址,
		                              //以免free掉現(xiàn)在的地址后,找不到后面的地址了
		free(cur);
		cur = temp;  //再重新找回來

	}

}




int main() {

	test1();
    //test2()
    //test3()

	return 0;
}

3、test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"

void test1()   //測試插入
{
	SListNode* plist = NULL;  //指向鏈表第一個節(jié)點(diǎn)的指針,因?yàn)殒湵頉]有節(jié)點(diǎn),所以初始化為NULL;

	//頭插
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPrint(plist);

	//尾插
	SListPushBack(&plist, 4);
	SListPushBack(&plist, 5);
	SListPushBack(&plist, 6);
	SListPrint(plist);

	//在pos位置前插入
	SListNode* pos = SListFind(plist, 5);
	if (pos != NULL)
	{
		SListInsert(&plist, pos, 50);
	}
	pos = SListFind(plist, 3);
	if (pos != NULL)
	{
		SListInsert(&plist, pos, 30);
	}
	SListPrint(plist);

	pos = SListFind(plist, 6);
	if (pos != NULL)
	{
		SListInsert(&plist, pos, 60);
	}
	SListPrint(plist);

	//在pos位置后插入
	pos = SListFind(plist, 1);
	if (pos != NULL)
	{
		SListInsertAfter(&plist, pos, 10);
	}
	pos = SListFind(plist, 3);
	if (pos != NULL)
	{
		SListInsertAfter(&plist, pos, 30);
	}
	SListPrint(plist);

	//銷毀
	SListDestory(&plist);
}

void test2()  //測試刪除
{
	SListNode* plist = NULL;  //指向鏈表第一個節(jié)點(diǎn)的指針,因?yàn)殒湵頉]有節(jié)點(diǎn),所以初始化為NULL;

	//頭插
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPrint(plist);

	//在頭部刪除數(shù)據(jù)
	//SListPopFront(&plist);
	//SListPopFront(&plist);
	//SListPopFront(&plist);
	//SListPrint(plist);

	//在尾部刪除數(shù)據(jù)
	//SListPopBack(&plist);
	//SListPopBack(&plist);
	//SListPopBack(&plist);
	//SListPrint(plist);

	//刪除pos位置處的數(shù)據(jù)
	//SLTNode* pos = SListFind(plist, 1);
	//if (pos != NULL)
	//{
	//	SListErase(&plist, pos);
	//}
	//pos = SListFind(plist, 3);
	//if (pos != NULL)
	//{
	//	SListErase(&plist, pos);
	//}
	//SListPrint(plist);
	//pos = SListFind(plist, 2);
	//if (pos != NULL)
	//{
	//	SListErase(&plist, pos);
	//}
	//SListPrint(plist);

	//刪除pos位置后的數(shù)據(jù)
	SListNode* pos = SListFind(plist, 2);
	if (pos != NULL)
	{
		SListEraseAfter(&plist, pos);
	}
	SListPrint(plist);
	pos = SListFind(plist, 3);
	if (pos != NULL)
	{
		SListEraseAfter(&plist, pos);
	}
	SListPrint(plist);

	//銷毀
	SListDestory(&plist);
}

void test3()  //測試查和改
{
	SListNode* plist = NULL;  //指向鏈表第一個節(jié)點(diǎn)的指針,因?yàn)殒湵頉]有節(jié)點(diǎn),所以初始化為NULL;

	//頭插
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPrint(plist);

	//查找并修改pos位置處的數(shù)據(jù)
	SListNode* pos = SListFind(plist, 3);
	if (pos != NULL)
	{
		SListModify(plist, pos, 30);
	}
	SListPrint(plist);
	pos = SListFind(plist, 1);
	if (pos != NULL)
	{
		SListModify(plist, pos, 10);
	}
	SListPrint(plist);

	//銷毀
	SListDestory(&plist);
}


到了這里,關(guān)于【數(shù)據(jù)結(jié)構(gòu)】單鏈表的增刪查改(C實(shí)現(xiàn))的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【數(shù)據(jù)結(jié)構(gòu)】實(shí)現(xiàn)單鏈表的增刪查

    【數(shù)據(jù)結(jié)構(gòu)】實(shí)現(xiàn)單鏈表的增刪查

    鏈表類和節(jié)點(diǎn)類的定義: 圖解: 從中間位置插入: 圖解:假定index=2 尾插: 刪除當(dāng)前線性表中索引為index的元素,返回刪除的元素值: 圖解: 刪除當(dāng)前線性表中第一個值為element的元素: 刪除當(dāng)前線性表中所有值為element的元素: 將當(dāng)前線性表中index位置的元素替換為eleme

    2024年02月14日
    瀏覽(917)
  • 數(shù)據(jù)結(jié)構(gòu)之順序表的增刪查改

    數(shù)據(jù)結(jié)構(gòu)之順序表的增刪查改

    自今日起,我們正式 越過C語言的大山 ,走向了 數(shù)據(jù)結(jié)構(gòu)的深山 ,現(xiàn)如今擺在我們面前的第一個坎就是 順序表 ,我們需要了解順序表的 定義 ,并且知道,如何對其進(jìn)行 增刪查改 ,之后我們需要在此處基礎(chǔ)上寫出一份 通訊錄代碼 ,ok,順序表,啟動! 線性表( linear lis

    2024年01月23日
    瀏覽(32)
  • 【數(shù)據(jù)結(jié)構(gòu)】鏈表:帶頭雙向循環(huán)鏈表的增刪查改

    【數(shù)據(jù)結(jié)構(gòu)】鏈表:帶頭雙向循環(huán)鏈表的增刪查改

    本篇要分享的內(nèi)容是帶頭雙向鏈表,以下為本片目錄 目錄 一、鏈表的所有結(jié)構(gòu) 二、帶頭雙向鏈表 2.1尾部插入 2.2哨兵位的初始化 2.3頭部插入 2.4 打印鏈表 2.5尾部刪除 2.6頭部刪除 ?2.7查找結(jié)點(diǎn) 2.8任意位置插入 2.9任意位置刪除? 在剛開始接觸鏈表的時候,我們所學(xué)僅僅所學(xué)的

    2024年02月05日
    瀏覽(29)
  • 數(shù)據(jù)結(jié)構(gòu)入門(C語言)順序表的增刪查改

    數(shù)據(jù)結(jié)構(gòu)入門(C語言)順序表的增刪查改

    本章會用C語言來描述數(shù)據(jù)結(jié)構(gòu)中的順序表,實(shí)現(xiàn)簡單的增刪查改操作,其中頭文件包含在新建的頭文件SeqList.h內(nèi),順序表的實(shí)現(xiàn)在新建的Seqlist.c內(nèi),主函數(shù)Text.c將會實(shí)現(xiàn)菜單,方便我們進(jìn)行功能的選擇。 順序表是用一段物理地址 連續(xù) 的存儲單元依次存儲數(shù)據(jù)元素的線性結(jié)構(gòu)

    2024年02月03日
    瀏覽(21)
  • 【數(shù)據(jù)結(jié)構(gòu)】單向鏈表的增刪查改以及指定pos位置的插入刪除

    【數(shù)據(jù)結(jié)構(gòu)】單向鏈表的增刪查改以及指定pos位置的插入刪除

    目錄 ?單向鏈表的概念及結(jié)構(gòu) ?尾插 頭插 尾刪 ?編輯 ?頭刪 ?查找 ?在pos位置前插 ?在pos位置后插 ?刪除pos位置 ?刪除pos的后一個位置 總結(jié) 代碼? 概念:鏈表是一種 物理存儲結(jié)構(gòu)上非連續(xù) 、非順序的存儲結(jié)構(gòu),數(shù)據(jù)元素的 邏輯順序 是通過鏈表中的 指針鏈接 次序?qū)崿F(xiàn)的

    2024年02月05日
    瀏覽(26)
  • 數(shù)據(jù)結(jié)構(gòu)---手撕順序表---順序表增刪查改尋找功能的實(shí)現(xiàn)

    順序表作為數(shù)據(jù)結(jié)構(gòu)的入門知識,整體知識較為簡單,主要對動態(tài)內(nèi)存開辟 結(jié)構(gòu)體 指針有要求,其余難度較低 順序表主要需要實(shí)現(xiàn)的有順序表的增刪查改和定向搜索銷毀等,具體實(shí)現(xiàn)函數(shù)如下 要實(shí)現(xiàn)順序表,就需要對順序表進(jìn)行定義,在c語言中通常使用結(jié)構(gòu)體進(jìn)行寫入,

    2024年02月16日
    瀏覽(24)
  • 數(shù)據(jù)結(jié)構(gòu):手撕順序表---順序表增刪查改尋找功能的實(shí)現(xiàn)

    順序表作為數(shù)據(jù)結(jié)構(gòu)的入門知識,整體知識較為簡單,主要對動態(tài)內(nèi)存開辟 結(jié)構(gòu)體 指針有要求,其余難度較低 順序表主要需要實(shí)現(xiàn)的有順序表的增刪查改和定向搜索銷毀等,具體實(shí)現(xiàn)函數(shù)如下 要實(shí)現(xiàn)順序表,就需要對順序表進(jìn)行定義,在c語言中通常使用結(jié)構(gòu)體進(jìn)行寫入,

    2024年02月15日
    瀏覽(21)
  • 詳解初階數(shù)據(jù)結(jié)構(gòu)之順序表(SeqList)——單文件文件實(shí)現(xiàn)SeqList的增刪查改

    詳解初階數(shù)據(jù)結(jié)構(gòu)之順序表(SeqList)——單文件文件實(shí)現(xiàn)SeqList的增刪查改

    目錄 一、線性表 二、順序表 2.1概念及結(jié)構(gòu) 2.2接口實(shí)現(xiàn) 2.3動態(tài)順序表的創(chuàng)建 2.3動態(tài)順序表的初始化 2.3.1傳值初始化 2.3.2傳址初始化 2.4動態(tài)順序表的清空 2.5動態(tài)順序表的擴(kuò)容 2.6動態(tài)順序表內(nèi)容的打印 三、動態(tài)順序表的使用 3.1尾插尾刪 3.1.1尾插 3.1.2尾刪 3.2頭插頭刪 3.2.1頭插

    2024年02月09日
    瀏覽(22)
  • 【數(shù)據(jù)結(jié)構(gòu)】單鏈表的實(shí)現(xiàn)

    【數(shù)據(jù)結(jié)構(gòu)】單鏈表的實(shí)現(xiàn)

    ??個人主頁:平凡的小蘇 ??學(xué)習(xí)格言:別人可以拷貝我的模式,但不能拷貝我不斷往前的激情 ??C語言專欄:https://blog.csdn.net/vhhhbb/category_12174730.html ??數(shù)據(jù)結(jié)構(gòu)專欄:https://blog.csdn.net/vhhhbb/category_12211053.html ? ? ? ? 家人們更新不易,你們的??點(diǎn)贊??和?關(guān)注?真的對我

    2023年04月09日
    瀏覽(93)
  • 【數(shù)據(jù)結(jié)構(gòu)】-- 單鏈表的實(shí)現(xiàn)

    【數(shù)據(jù)結(jié)構(gòu)】-- 單鏈表的實(shí)現(xiàn)

    在前面我們學(xué)習(xí)了順序表,順序表在數(shù)組的基礎(chǔ)上提供了很多現(xiàn)成的方法,方便了我們對數(shù)據(jù)的管理,但是我們也發(fā)現(xiàn)順序表有著許多不足: 在處理大型的數(shù)據(jù)時,需要頻繁的增容且在中間刪除或插入數(shù)據(jù)時需要遍歷順序表,這些性質(zhì)導(dǎo)致了順序表的 效率較低 。這時我們就

    2024年04月27日
    瀏覽(110)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包