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

【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事

這篇具有很好參考價(jià)值的文章主要介紹了【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事,數(shù)據(jù)結(jié)構(gòu),保姆級教學(xué),數(shù)據(jù)結(jié)構(gòu),c語言,算法,改行學(xué)it

導(dǎo)言

大家好,很高興又和大家見面啦!?。?br> 在上一個(gè)篇章中,我們詳細(xì)的介紹了隊(duì)列的順序存儲結(jié)構(gòu)——循環(huán)隊(duì)列。同時(shí)花費(fèi)了大量的篇幅來介紹循環(huán)隊(duì)列的實(shí)現(xiàn)邏輯與實(shí)現(xiàn)方式,最后我們還使用C語言通過兩種方式是實(shí)現(xiàn)了循環(huán)隊(duì)列,相信大家看完上一篇內(nèi)容的話應(yīng)該對循環(huán)隊(duì)列及其基本操作的實(shí)現(xiàn)已經(jīng)沒什么問題了。如果對這些內(nèi)容還有疑問的朋友可以在文章下方評論留言,或者私信博主,博主看到后都會回復(fù)的哦!

在介紹順序存儲結(jié)構(gòu)時(shí)我們會發(fā)現(xiàn)對于循環(huán)隊(duì)列而言也是會有隊(duì)滿的情況,下面我們來想象一個(gè)實(shí)際情景:

現(xiàn)在我們要做一家奶茶店的在線下單管理系統(tǒng),按正常的流量來說,每天可能只有200——300用戶會來下單,而且也是陸陸續(xù)續(xù)的,那我們通過隊(duì)列來實(shí)現(xiàn)的話只需要申請300的空間就足夠應(yīng)付了。但是有一天因?yàn)橐恍┟朗巢┲鞯耐扑],這家店突然火了,每天的客流量可能達(dá)到500甚至更多,500人同時(shí)在線下單,此時(shí)就會出現(xiàn)滿隊(duì)的情況,就會導(dǎo)致前面300人下單不受影響,但是之后的200人只有在前面的訂單完成后才能成功下單。

在上面的這個(gè)情景中,如果經(jīng)常性的讓用戶出現(xiàn)無法下單的問題,這樣即使你這家店再火也是會對不能成功下單的顧客造成不好的體驗(yàn),所以我們就需要想個(gè)辦法來解決這個(gè)問題,這里我們就可以通過對內(nèi)存空間使用更加靈活的鏈?zhǔn)酱鎯硗瓿伞?/p>

所以在今天的內(nèi)容中,我們將要詳細(xì)介紹一下隊(duì)列的鏈?zhǔn)酱鎯Α滉?duì)列。

一、鏈隊(duì)列

通過鏈?zhǔn)酱鎯?shí)現(xiàn)的隊(duì)列稱之為鏈隊(duì)列。

前面在介紹單鏈表時(shí),我們有介紹過對于一個(gè)單鏈表而言,它在創(chuàng)建時(shí),既可以通過從隊(duì)頭插入新的結(jié)點(diǎn)來創(chuàng)建一個(gè)單鏈表,也可以通過從隊(duì)尾插入新的結(jié)點(diǎn)來創(chuàng)建一個(gè)單鏈表,當(dāng)我們單鏈表創(chuàng)建好后,想要插入新的元素或者刪除一個(gè)元素我們是可以在任意位置實(shí)現(xiàn)插入和刪除的。

現(xiàn)在對于隊(duì)列而言它已經(jīng)限制了插入與刪除的位置——只能從隊(duì)尾入隊(duì),從隊(duì)頭出隊(duì)。大家能不能聯(lián)想到什么呢?

沒錯(cuò)如果我們要實(shí)現(xiàn)一個(gè)鏈隊(duì)列的話,實(shí)際上就是實(shí)現(xiàn)了一個(gè)只能進(jìn)行尾插操作與頭刪操作的單鏈表。也就是說,鏈隊(duì)列的本質(zhì)就是一個(gè)操作受限的單鏈表,在這個(gè)單鏈表中它同時(shí)擁有指向表頭元素的指針與指向表尾元素的指針。

在明確了鏈隊(duì)列的本質(zhì)后,下面我們就可以開始來通過C語言實(shí)現(xiàn)鏈隊(duì)列了。

二、鏈隊(duì)列的基本操作的實(shí)現(xiàn)

我們在實(shí)現(xiàn)一個(gè)鏈隊(duì)列時(shí),同樣是一個(gè)從無到有的過程,也就是,我們也是從定義數(shù)據(jù)類型、初始化、判空、入隊(duì)、出隊(duì)、查找、銷毀等基本操作進(jìn)行一一介紹。

這時(shí)可能就有朋友說了,你這里怎么沒有判滿呢?

這就是鏈隊(duì)列相比于循環(huán)隊(duì)列比較友好的地方,因?yàn)殒滉?duì)列是和單鏈表一樣在進(jìn)行插入操作時(shí)才會申請一片新的空間,理論上來說,只要你的內(nèi)存空間足夠大,它就不存在滿隊(duì)的情況,它能夠一直插入新的元素。

現(xiàn)在我們就來看一下鏈隊(duì)列的第一個(gè)內(nèi)容——數(shù)據(jù)類型的定義;

2.1 鏈隊(duì)列的數(shù)據(jù)類型

鏈隊(duì)列的數(shù)據(jù)類型的定義,我們需要先弄清楚它所擁有的元素,如下所示:
【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事,數(shù)據(jù)結(jié)構(gòu),保姆級教學(xué),數(shù)據(jù)結(jié)構(gòu),c語言,算法,改行學(xué)it
從圖中我們可以看到,對于一個(gè)鏈隊(duì)列而言,它是由存儲數(shù)據(jù)元素以及指向下一個(gè)元素的結(jié)點(diǎn)和隊(duì)頭指針與隊(duì)尾指針組成,那是不是就是說我們應(yīng)該將數(shù)據(jù)類型定義為下面這種形式呢?

//鏈隊(duì)列的數(shù)據(jù)類型
typedef int ElemType;
typedef struct LinkQueue {
	ElemType data;//數(shù)據(jù)域
	struct LinkQueue* next;//指針域
	struct LinkQueue* front, * rear;//隊(duì)頭指針與隊(duì)尾指針
}LinkQueue;

對于這種形式的數(shù)據(jù)類型,我們通過該數(shù)據(jù)類型來定義一個(gè)隊(duì)列看一下它會什么樣的情況:
【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事,數(shù)據(jù)結(jié)構(gòu),保姆級教學(xué),數(shù)據(jù)結(jié)構(gòu),c語言,算法,改行學(xué)it
從監(jiān)視窗口中我們可以看到,通過這個(gè)類型定義出來的隊(duì)列它是擁有三個(gè)指針域和一個(gè)數(shù)據(jù)域的結(jié)點(diǎn),如果畫圖展示的話那就是如下結(jié)構(gòu):
【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事,數(shù)據(jù)結(jié)構(gòu),保姆級教學(xué),數(shù)據(jù)結(jié)構(gòu),c語言,算法,改行學(xué)it
這個(gè)與我們需要的差距還是挺大的,那就是說明我們通過這種方式定義的數(shù)據(jù)類型是錯(cuò)誤的,那我們應(yīng)該如何定義的?

我們再來回顧一下我們的需求——1.存儲數(shù)據(jù)元素和指向下一個(gè)元素的結(jié)點(diǎn),2.擁有隊(duì)頭指針和隊(duì)尾指針。

對于這兩個(gè)需求,我們可以不可以將其拆解為兩個(gè)內(nèi)容呢?也就是將結(jié)點(diǎn)單獨(dú)拎出來,將隊(duì)頭指針與隊(duì)尾指針單獨(dú)領(lǐng)出來,并且,隊(duì)頭指針和隊(duì)尾指針是指向結(jié)點(diǎn)的兩個(gè)指針,對應(yīng)的數(shù)據(jù)類型,我們就可以將其描述為:

//鏈隊(duì)列的數(shù)據(jù)類型
typedef int ElemType;
typedef struct LinkNode {
	ElemType data;//數(shù)據(jù)域
	struct LinkNode* next;//指針域
}LinkNode;//定義隊(duì)列中的每一個(gè)結(jié)點(diǎn)
typedef struct LinkQueue{
	LinkNode* front, * rear;//隊(duì)頭指針與隊(duì)尾指針
}LinkQueue;//定義隊(duì)列的隊(duì)頭指針與隊(duì)尾指針

所謂實(shí)踐才是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),我們可以直接通過監(jiān)視窗口來看一下是否如我們所需要的那樣:
【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事,數(shù)據(jù)結(jié)構(gòu),保姆級教學(xué),數(shù)據(jù)結(jié)構(gòu),c語言,算法,改行學(xué)it
可以看到,此時(shí)就很好的滿足了我們的需求,一個(gè)鏈隊(duì)列中擁有隊(duì)頭指針與隊(duì)尾指針,并且兩個(gè)指針分別指向存儲數(shù)據(jù)元素的結(jié)點(diǎn)。

現(xiàn)在我們就已經(jīng)定義好了鏈隊(duì)列的數(shù)據(jù)類型,下面就可以開始給鏈隊(duì)進(jìn)行初始化了;

2.2 鏈隊(duì)列的初始化

對于鏈隊(duì)列而言,它既然本質(zhì)是單鏈表,那么它就能有兩種方式來實(shí)現(xiàn)——帶頭結(jié)點(diǎn)和不帶頭結(jié)點(diǎn)。

在單鏈表中我們就已經(jīng)介紹過這兩種方式的差異,現(xiàn)在我們再來回顧一下:

  • 帶頭結(jié)點(diǎn)的單鏈表的好處就是我們在初始化階段,鏈表的頭指針就已經(jīng)有了具體指向的空間,因此在后續(xù)的插入與刪除操作中,就不需要在分情況來討論了;
  • 而不帶頭結(jié)點(diǎn)的單鏈表,在初始化階段,頭結(jié)點(diǎn)是沒有指向?qū)ο蟮?,因此需要初始化為空指針,在后續(xù)的插入操作中因?yàn)轭^指針為空指針的原因,所以我們還需要將它這種情況分開處理;

從這兩種的操作上來看帶頭結(jié)點(diǎn)的單鏈表會跟更加的簡便一點(diǎn),而且如果我們想要計(jì)算表長,還可以通過頭結(jié)點(diǎn)的數(shù)據(jù)域來進(jìn)行記錄,不過這里我們還是將兩種情況的初始化都給大家展示一下,大家后面自己在實(shí)現(xiàn)時(shí)可以依照自己的需求來選擇實(shí)現(xiàn)的方式;

2.2.1 帶頭結(jié)點(diǎn)的鏈隊(duì)列的初始化

帶頭結(jié)點(diǎn)的鏈隊(duì)列與帶頭結(jié)點(diǎn)的單鏈表一樣,初始化時(shí)頭結(jié)點(diǎn)的指針域初始化為空指針,與單鏈表不同的是鏈隊(duì)列的頭指針被隊(duì)頭指針代替,并新增了隊(duì)尾指針,如下所示:
【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事,數(shù)據(jù)結(jié)構(gòu),保姆級教學(xué),數(shù)據(jù)結(jié)構(gòu),c語言,算法,改行學(xué)it
將這個(gè)結(jié)構(gòu)用C語言描述則是:

//帶頭結(jié)點(diǎn)的鏈隊(duì)列的初始化
bool InitQueue(LinkQueue* Q) {
	if (!Q)
		return false;
	//為頭結(jié)點(diǎn)申請空間
	Q->rear = (LinkNode*)calloc(1, sizeof(LinkNode));
	if (!Q->rear)
		return false;
	//初始化頭結(jié)點(diǎn)
	Q->rear->next = NULL;
	 //初始化隊(duì)尾指針
	Q->front = Q->rear;//隊(duì)尾指針與隊(duì)頭指針同時(shí)指向頭結(jié)點(diǎn)
	return true;
}

對于帶頭結(jié)點(diǎn)的鏈隊(duì)列的初始化有幾個(gè)問題可能會被大家忽視掉,下面我們一起來看一下;

  1. 在鏈隊(duì)列的初始化中,我們可不可以通過鏈隊(duì)列Q來申請一塊空間?

這個(gè)答案肯定是不可以的。為了解答這個(gè)問題,我們首先需要搞清楚我們申請空間的目的是什么?

我們申請一塊空間是為了存放數(shù)據(jù)元素,以及存放下一個(gè)元素的位置,因此,我們實(shí)際上申請的是一塊結(jié)點(diǎn)空間,而不是鏈隊(duì)列的空間,當(dāng)我們通過鏈隊(duì)列的數(shù)據(jù)類型創(chuàng)建好鏈隊(duì)列Q之后,我們是可以直接通過Q來訪問對應(yīng)的頭尾指針的,并且我們只能通過頭尾指針來找到對應(yīng)的結(jié)點(diǎn)。

因此我們不能通過鏈隊(duì)列Q來申請空間。

  1. 在鏈隊(duì)列的初始化中,我們可不可以通過隊(duì)頭指針來申請空間?

這個(gè)答案也是不可以的。這個(gè)問題的依據(jù)是隊(duì)列的操作特性——先進(jìn)先出(FIFO)。隊(duì)列中的元素只能從隊(duì)尾入隊(duì),只能從隊(duì)頭出隊(duì),因此,我們在為頭結(jié)點(diǎn)申請空間時(shí),也需要遵從這個(gè)特性。
在鏈隊(duì)列中,隊(duì)尾指針的作用是用來插入新的結(jié)點(diǎn),而隊(duì)頭指針的作用是用來刪除結(jié)點(diǎn),所以我們只能通過隊(duì)尾指針來申請頭結(jié)點(diǎn)的空間并插入到隊(duì)列中

現(xiàn)在大家對這種帶頭結(jié)點(diǎn)的初始化應(yīng)該是了解了,下面我們再來看一下,如果是不帶頭結(jié)點(diǎn)的鏈隊(duì)列,我們又應(yīng)該如何初始化;

2.2.3 不帶頭結(jié)點(diǎn)的鏈隊(duì)列的初始化

如果一個(gè)鏈隊(duì)列它不帶頭結(jié)點(diǎn)的話也就是在創(chuàng)建好隊(duì)列后,此時(shí)的隊(duì)頭指針與隊(duì)尾指針是不指向任何對象的。在這種情況下,為了防止隊(duì)頭指針與隊(duì)尾指針變成野指針,我們需要做的就是將其初始化為空指針,代碼如下所示:

//不帶頭結(jié)點(diǎn)的鏈隊(duì)列的初始化
bool InitQueue2(LinkQueue* Q) {
	if (!Q)
		return false;
	//初始化隊(duì)頭指針與隊(duì)尾指針
	Q->front = Q->rear = NULL;
	return true;
}

對于初始化的實(shí)現(xiàn),我們只要明確要初始化的對象以及初始化的內(nèi)容,基本上問題不大。在完成初始化之后,我們可以隨帶實(shí)現(xiàn)的就是判空操作,下面我們來探討一下對于不同形式的鏈隊(duì)列,我們應(yīng)該如何實(shí)現(xiàn)判空;

2.3 鏈隊(duì)列的判空

在循環(huán)隊(duì)列中,因?yàn)闈M隊(duì)時(shí)的情況會發(fā)生變化,所以根據(jù)形式的實(shí)現(xiàn),我們需要關(guān)注的就是隊(duì)頭指針與隊(duì)尾指針在邏輯上的相對位置,而對于鏈隊(duì)列而言,它并不存在滿隊(duì)的情況,因此它的空隊(duì)列的情況是固定的,不同形式實(shí)現(xiàn)的鏈隊(duì)列的判空與對應(yīng)的初始化是相同的,下面我們就來一一介紹一下;

2.3.1 帶頭結(jié)點(diǎn)的鏈隊(duì)列的判空

當(dāng)我們通過帶頭結(jié)點(diǎn)的單鏈表實(shí)現(xiàn)隊(duì)列時(shí),在初始化階段,隊(duì)頭指針與隊(duì)尾指針指向的是同一個(gè)結(jié)點(diǎn),當(dāng)我們插入新的結(jié)點(diǎn)時(shí),隊(duì)頭指針指向的對象不會改變,依舊是頭結(jié)點(diǎn),唯一有改變的就是隊(duì)尾指針,如下所示:
【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事,數(shù)據(jù)結(jié)構(gòu),保姆級教學(xué),數(shù)據(jù)結(jié)構(gòu),c語言,算法,改行學(xué)it
也就是說,當(dāng)隊(duì)尾指針與隊(duì)頭指針指向同一個(gè)對象時(shí),那就說明此時(shí)的隊(duì)列為空隊(duì)列,因此,我們就可以編寫代碼:

//帶頭結(jié)點(diǎn)的鏈隊(duì)列的判空
bool EmptyQueue(LinkQueue Q) {
	if (Q.front == Q.rear)
		return true;
	return false;
}

對于參數(shù)的使用,一定要記住下面這個(gè)點(diǎn):

  • 當(dāng)我們需要對實(shí)參進(jìn)行修改時(shí),就需要傳址傳參,對應(yīng)的形參需要通過指針接收參數(shù),就如我們在初始化階段,會對實(shí)參進(jìn)行修改,所以形參是通過指針進(jìn)行接收;
  • 當(dāng)我們不需要對實(shí)參進(jìn)行修改時(shí),可以只用傳值傳參,對應(yīng)的形參正常接收即可,就像我們這里的判空,我們不會對實(shí)參有任何的修改,因此,正常使用與實(shí)參相對應(yīng)的形參即可;

2.3.2 不帶頭結(jié)點(diǎn)的鏈隊(duì)列的判空

當(dāng)我們通過不帶頭結(jié)點(diǎn)的單鏈表實(shí)現(xiàn)一個(gè)鏈隊(duì)列時(shí),此時(shí)的鏈隊(duì)列和單鏈表一樣,也是一個(gè)從無到有的過程,也就是說,在沒有任何元素時(shí),不管是隊(duì)頭指針還是隊(duì)尾指針,它們此時(shí)都應(yīng)該是空指針,但是當(dāng)有了第一個(gè)元素之后,它們會同時(shí)指向第一個(gè)元素的結(jié)點(diǎn),如下所示:
【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事,數(shù)據(jù)結(jié)構(gòu),保姆級教學(xué),數(shù)據(jù)結(jié)構(gòu),c語言,算法,改行學(xué)it
因此如果我們要對一個(gè)不帶頭結(jié)點(diǎn)的鏈隊(duì)列進(jìn)行判空操作,我們只需要判斷隊(duì)頭指針或者隊(duì)尾指針是否為空指針即可,如下所示:

//不帶頭結(jié)點(diǎn)的鏈隊(duì)列的判空
bool EmptyQueue2(LinkQueue Q) {
	//通過隊(duì)頭指針判斷
	if (Q.front == NULL)//可以簡寫成if(!Q.front)
		return true;
	//或者通過隊(duì)尾指針判斷
	//if (Q.rear == NULL)//可以簡寫成if(!Q.rear)
	//	return true;
	return false;
}

這里的判斷條件可以通過使用邏輯反操作符來進(jìn)行簡寫,由于結(jié)構(gòu)體成員訪問操作符的優(yōu)先級高于邏輯反操作符的優(yōu)先級,因此我們可以直接寫成!Q.front這種形式;

2.4 鏈隊(duì)列的入隊(duì)

在完成了初始化后,我們就可以對鏈隊(duì)列進(jìn)行入隊(duì)的操作了,前面也有介紹過兩種實(shí)現(xiàn)方式的區(qū)別,這里我們還是一一的進(jìn)行詳細(xì)的介紹;

2.4.1 帶頭結(jié)點(diǎn)的鏈隊(duì)列的入隊(duì)

對于帶頭結(jié)點(diǎn)的鏈隊(duì)列而言,我們在進(jìn)行入隊(duì)操作時(shí),需要做的就是插入新的結(jié)點(diǎn),并改變隊(duì)尾指針的指向?qū)ο?,由于?duì)列的操作特性是只能從隊(duì)尾入隊(duì),因此,我們在實(shí)現(xiàn)鏈隊(duì)列時(shí),也只能通過尾插法的方式進(jìn)行入隊(duì),這里我就不展示對應(yīng)的圖片了,下面直接來看代碼:

//帶頭結(jié)點(diǎn)的鏈隊(duì)列的入隊(duì)
bool EnQueue(LinkQueue* Q, ElemType x) {
	if (!Q)
		return false;
	//為新的結(jié)點(diǎn)申請空間
	LinkNode* p = (LinkNode*)calloc(1, sizeof(LinkNode));
	if (!p)
		return false;
	p->data = x;//將數(shù)據(jù)存入新結(jié)點(diǎn)的數(shù)據(jù)域中
	p->next = Q->rear->next;//將新結(jié)點(diǎn)的指針域指向隊(duì)尾結(jié)點(diǎn)的后繼對象,也就是NULL
	Q->rear->next = p;//隊(duì)尾結(jié)點(diǎn)的后繼指針指向新結(jié)點(diǎn),完成插入操作
	Q->rear = p;//隊(duì)尾指針指向新的結(jié)點(diǎn)
	return true;
}

既然是單鏈表,那么后插的操作就是通過結(jié)點(diǎn)指針域的修改實(shí)現(xiàn)的插入,這里需要注意的是,因?yàn)椴迦氲男陆Y(jié)點(diǎn)肯定是在隊(duì)尾進(jìn)行插入操作的,所以我們可以直接將新結(jié)點(diǎn)的指針域置為空指針,然后只對隊(duì)尾結(jié)點(diǎn)的指針域以及隊(duì)尾指針進(jìn)行修改就行,如下所示:

	p->next = NULL;//新結(jié)點(diǎn)的指針域置為空
	Q->rear->next = p;//隊(duì)尾結(jié)點(diǎn)的后繼指針指向新結(jié)點(diǎn),完成插入操作
	Q->rear = p;//隊(duì)尾指針指向新的結(jié)點(diǎn)

這種插入操作的效果是一樣的,只不過我通過上面的寫法,能夠更加直觀的感受到結(jié)點(diǎn)插入的過程,代碼的可讀性也會更強(qiáng)一點(diǎn);

2.4.2 不帶頭結(jié)點(diǎn)的鏈隊(duì)列的入隊(duì)

對于不帶頭結(jié)點(diǎn)的鏈隊(duì)列,我們在進(jìn)行入隊(duì)操作時(shí),是從無到有的過程,這就導(dǎo)致了插入第一個(gè)結(jié)點(diǎn)時(shí)的處理方式與插入后續(xù)結(jié)點(diǎn)時(shí)的處理方式上有所差異,所以我們需要分情況討論,如下所示:

//不帶頭結(jié)點(diǎn)的鏈隊(duì)列的入隊(duì)
bool EnQueue2(LinkQueue* Q, ElemType x) {
	if (!Q)
		return false;
	if (EmptyQueue2(*Q)) {
		Q->rear = (LinkNode*)calloc(1, sizeof(LinkNode));
		if (!Q->rear)
			return false;
		Q->rear->data = x;//將數(shù)據(jù)存入新結(jié)點(diǎn)中
		Q->rear->next = NULL;//將新結(jié)點(diǎn)的后繼指針置為空
		Q->front = Q->rear;//將隊(duì)頭指針指向新結(jié)點(diǎn)
	}
	else {
		LinkNode* p = (LinkNode*)calloc(1, sizeof(LinkNode));
		if (!p)
			return false;
		p->data = x;//將數(shù)據(jù)存入新結(jié)點(diǎn)中
		p->next = Q->rear->next;//將新結(jié)點(diǎn)的后繼指針指向隊(duì)尾結(jié)點(diǎn)的后繼對象
		Q->rear->next = p;//隊(duì)尾結(jié)點(diǎn)的后繼指針指向新結(jié)點(diǎn)完成插入操作
		Q->rear = p;//隊(duì)尾結(jié)點(diǎn)指向新結(jié)點(diǎn)
	}
	return true;
}

可以看到,對于不帶頭結(jié)點(diǎn)的鏈隊(duì)列,在進(jìn)行插入操作時(shí)相比于帶頭結(jié)點(diǎn)的鏈隊(duì)列會麻煩一點(diǎn),每次在執(zhí)行插入操作時(shí),都需要完成一次判斷,檢查隊(duì)列是否為空,相比于帶頭結(jié)點(diǎn)的鏈隊(duì)列來說會稍微麻煩一點(diǎn);

2.5 鏈隊(duì)列的出隊(duì)

鏈隊(duì)列的出隊(duì)操作對于兩種方式的實(shí)現(xiàn)也是有一定的差異,下面我們一起來看一下,如何實(shí)現(xiàn)這兩種鏈隊(duì)列,以及它們之間的差異在哪里;

2.5.1 帶頭結(jié)點(diǎn)的鏈隊(duì)列的出隊(duì)

對于帶頭結(jié)點(diǎn)的鏈隊(duì)列來說,我們在進(jìn)行出隊(duì)時(shí),是對隊(duì)頭元素的結(jié)點(diǎn)進(jìn)行出隊(duì),而不是對頭結(jié)點(diǎn)進(jìn)行出隊(duì),因此我們只需要實(shí)現(xiàn)簡單的頭刪操作即可,如下所示:

//帶頭結(jié)點(diǎn)的鏈隊(duì)列的出隊(duì)
bool DeQueue(LinkQueue* Q, ElemType* x) {
	if (!Q && !x)
		return false;
	if (EmptyQueue(*Q))
		return false;
	LinkNode* p = Q->front->next;//指向隊(duì)頭元素的指針
	Q->front->next = p->next;//修改頭結(jié)點(diǎn)的后繼結(jié)點(diǎn)
	if (Q->rear == p)//判斷此時(shí)的隊(duì)頭結(jié)點(diǎn)的后繼結(jié)點(diǎn)是否為隊(duì)尾結(jié)點(diǎn)
		Q->rear = Q->front;//當(dāng)為隊(duì)尾結(jié)點(diǎn)時(shí),移動(dòng)隊(duì)尾指針
	p->next = NULL;//隊(duì)頭元素結(jié)點(diǎn)完成出隊(duì)操作
	*x = p->data;//將出隊(duì)的元素通過變量x帶回到主函數(shù)中
	free(p);//釋放該結(jié)點(diǎn)占用的內(nèi)存空間
	p = NULL;//將指針置空防止出現(xiàn)野指針
	return true;
}

出隊(duì)操作實(shí)際上就是對頭結(jié)點(diǎn)的后繼結(jié)點(diǎn)進(jìn)行更改,并釋放被刪除的結(jié)點(diǎn)的空間,而對于帶回出隊(duì)元素的操作,這個(gè)不做強(qiáng)制要求,根據(jù)實(shí)際需求進(jìn)行就行,但是在考試中,這個(gè)是需要加上的;

但是有一點(diǎn)需要注意,不管是帶頭結(jié)點(diǎn)的隊(duì)列進(jìn)行出隊(duì)還是不帶頭結(jié)點(diǎn)的隊(duì)列進(jìn)行出隊(duì),能夠執(zhí)行出隊(duì)操作的前提肯定是隊(duì)列不為空,因此我們需要在出隊(duì)操作前加入一次判空隊(duì)列的操作;

因?yàn)槲覀冊阪滉?duì)列中使用的是兩個(gè)指針——隊(duì)頭指針與隊(duì)尾指針,因此,我們在進(jìn)行出隊(duì)操作時(shí),還要注意隊(duì)尾指針的位置,當(dāng)隊(duì)列中只剩最后一個(gè)結(jié)點(diǎn)時(shí),我們執(zhí)行完出隊(duì)操作后,隊(duì)列會變?yōu)榭贞?duì)列,所以在進(jìn)行出隊(duì)前,我們需要先改變隊(duì)尾指針的指向,使其與隊(duì)頭指針一同指向頭結(jié)點(diǎn);

2.5.2 不帶頭結(jié)點(diǎn)的鏈隊(duì)列的出隊(duì)

在不帶頭結(jié)點(diǎn)的鏈隊(duì)列中,我們執(zhí)行頭刪操作時(shí),修改的是隊(duì)頭指針的指向?qū)ο螅渌僮鞫枷嗤?,對?yīng)的代碼如下所示:

//不帶頭結(jié)點(diǎn)的鏈隊(duì)列的出隊(duì)
bool DeQueue2(LinkQueue* Q, ElemType* x) {
	if (!Q && !x)
		return false;
	if (EmptyQueue2(*Q))
		return false;
	LinkNode* p = Q->front;//指向隊(duì)頭結(jié)點(diǎn)的指針
	Q->front = p->next;//隊(duì)頭指針指向隊(duì)頭結(jié)點(diǎn)的后繼結(jié)點(diǎn)
	if (Q->rear == p)//判斷此時(shí)的隊(duì)頭結(jié)點(diǎn)是否為隊(duì)尾結(jié)點(diǎn)
		Q->rear = NULL;//當(dāng)為隊(duì)尾結(jié)點(diǎn)時(shí),需要移動(dòng)隊(duì)尾指針
	p->next = NULL;//隊(duì)頭結(jié)點(diǎn)完成出隊(duì)操作
	*x = p->data;//將出隊(duì)的元素帶回主函數(shù)中
	free(p);//釋放隊(duì)頭結(jié)點(diǎn)的內(nèi)存空間
	p = NULL;//將指針p置為空,防止出現(xiàn)野指針
	return true;
}

在不帶頭結(jié)點(diǎn)的鏈隊(duì)列中,對隊(duì)尾結(jié)點(diǎn)刪除的處理也是有別于帶頭結(jié)點(diǎn)的鏈隊(duì)列的,造成這個(gè)區(qū)別的原因就是因?yàn)閮煞N鏈隊(duì)列的空隊(duì)列是不一樣的,不帶頭結(jié)點(diǎn)的鏈隊(duì)列的空隊(duì)列是隊(duì)頭指針與隊(duì)尾指針都是空指針,因此這里我們需要在進(jìn)行最后一個(gè)結(jié)點(diǎn)出隊(duì)時(shí)將隊(duì)尾指針置為空指針;

2.6 鏈隊(duì)列的查找

對于查找操作而言,我們只能在出隊(duì)的一邊進(jìn)行查找,就好比自動(dòng)販賣機(jī),當(dāng)我們想要購買飲料時(shí),我們只能獲得一列飲料中的第一瓶飲料,而無法越過第一瓶直接找到后面的飲料,因此,查找操作就是訪問的隊(duì)頭元素。下面我們來看一下不同形式的鏈隊(duì)列如何實(shí)現(xiàn)查找操作;

2.6.1 帶頭結(jié)點(diǎn)的鏈隊(duì)列的查找

在帶頭結(jié)點(diǎn)的鏈隊(duì)列中,我們要查找時(shí),是通過頭結(jié)點(diǎn)來訪問隊(duì)頭元素,對應(yīng)的代碼如下所示:

//帶頭結(jié)點(diǎn)的鏈隊(duì)列的查找
bool GetHead(LinkQueue Q, ElemType* x) {
	if (!x)
		return false;
	if (EmptyQueue(Q))
		return false;
	*x = Q.front->next->data;//通過頭結(jié)點(diǎn)找到隊(duì)頭元素的結(jié)點(diǎn)
	return true;
}

當(dāng)然,我們能成功訪問隊(duì)頭結(jié)點(diǎn)的前提是隊(duì)列不為空,所以在訪問前,我們需要先判斷隊(duì)列是否為空;

2.6.2 不帶頭結(jié)點(diǎn)的鏈隊(duì)列的查找

當(dāng)隊(duì)列不帶頭結(jié)點(diǎn)時(shí),隊(duì)頭指針指向的就是隊(duì)頭結(jié)點(diǎn),因此我們可以直接通過隊(duì)頭指針來進(jìn)行訪問,對應(yīng)代碼如下所示:

//不帶頭結(jié)點(diǎn)的鏈隊(duì)列的查找
bool GetHead2(LinkQueue Q, ElemType* x) {
	if (!x)
		return false;
	if (EmptyQueue2(Q))
		return false;
	*x = Q.front->data;//通過隊(duì)頭指針訪問隊(duì)頭結(jié)點(diǎn)
	return true;
}

鏈隊(duì)列的查找操作對于兩種鏈隊(duì)列都是一樣的,我們只要分清訪問隊(duì)頭結(jié)點(diǎn)的對象就行;

2.7 鏈隊(duì)列的銷毀

當(dāng)我們要銷毀一個(gè)隊(duì)列時(shí),我們需要注意幾點(diǎn):

  1. 銷毀的目的是釋放并回收隊(duì)列向內(nèi)存申請的空間;
  2. 隊(duì)列的銷毀操作是通過重復(fù)的出隊(duì)操作實(shí)現(xiàn)的;
  3. 隊(duì)列在完成銷毀前需要先完成置空,最后再回收隊(duì)列的空間;
  4. 鏈隊(duì)列的空間與鏈隊(duì)列的結(jié)點(diǎn)的空間回收方式是不相同的;

上面這些點(diǎn)如果能弄清的話,那銷毀操作的實(shí)現(xiàn)就并不復(fù)雜了,下面我們一起來看一下這兩種鏈隊(duì)列的銷毀的實(shí)現(xiàn)方式;

2.7.1 帶頭結(jié)點(diǎn)的鏈隊(duì)列的銷毀

帶頭結(jié)點(diǎn)的鏈隊(duì)列的銷毀操作分為兩步——1.出隊(duì)所有的元素結(jié)點(diǎn),2.釋放頭結(jié)點(diǎn)的空間。具體實(shí)現(xiàn)的代碼如下所示:

//帶頭結(jié)點(diǎn)的鏈隊(duì)列的銷毀
bool DestroyQueue(LinkQueue* Q) {
	if (!Q)
		return false;
	while (Q->front->next) {
		ElemType x = 0;
		if (DeQueue(Q, &x)) 
			printf("元素%d所在結(jié)點(diǎn)完成出隊(duì)\n", x);
		else {
			printf("當(dāng)前結(jié)點(diǎn)出隊(duì)失敗\n");
			break;//當(dāng)出隊(duì)失敗時(shí)退出循環(huán)
		}
	}
	if (EmptyQueue(*Q))//在結(jié)束循環(huán)后,對隊(duì)列進(jìn)行判空
		printf("當(dāng)前隊(duì)列為空隊(duì)列\(zhòng)n");
	else {
		printf("當(dāng)前隊(duì)列為非空隊(duì)列\(zhòng)n");
		return false;
	}
	free(Q->front);//釋放頭結(jié)點(diǎn)的內(nèi)存空間
	Q->rear = Q->front = NULL;//將隊(duì)頭指針與隊(duì)尾指針置為空,完成銷毀操作
	return true;
}

在帶頭結(jié)點(diǎn)的鏈隊(duì)列中,我們通過指向隊(duì)頭結(jié)點(diǎn)的指針平來進(jìn)行各個(gè)元素結(jié)點(diǎn)的銷毀,在各元素結(jié)點(diǎn)銷毀的過程中會遇到兩種情況:

  1. 隊(duì)列為空隊(duì)列;
  2. 隊(duì)列非空且沒有正常銷毀;

當(dāng)遇到上述兩種情況,我們都無法繼續(xù)進(jìn)行出隊(duì)操作,因此在跳出循環(huán)后我們需要通過判空操作來判斷退出循環(huán)的原因,并給予對應(yīng)的反饋:

  • 當(dāng)隊(duì)列為空時(shí),說明一切正常,我們此時(shí)只需要釋放頭結(jié)點(diǎn)的空間,并在完成釋放后將隊(duì)頭指針與隊(duì)尾指針置為空,避免出現(xiàn)野指針,最后返回true來告知用戶成功銷毀;
  • 當(dāng)隊(duì)列不為空時(shí),說明出現(xiàn)了問題,此時(shí)我們需要直接返回false;

我們通過對不同情況的反饋結(jié)過來提高代碼的健壯性;

2.7.2 不帶頭結(jié)點(diǎn)的鏈隊(duì)列的銷毀

對于不帶頭結(jié)點(diǎn)的鏈隊(duì)列,當(dāng)所有元素的結(jié)點(diǎn)全部銷毀完后,對應(yīng)的隊(duì)頭指針與隊(duì)尾指針就直接變?yōu)榱丝罩羔槪虼怂匿N毀操作的代碼如下所示:

//不帶頭結(jié)點(diǎn)的鏈隊(duì)列的銷毀
bool DestroyQueue2(LinkQueue* Q) {
	if (!Q)
		return false;
	while (Q->front) {
		ElemType x = 0;
		if (DeQueue2(Q, &x)) 
			printf("元素%d所在結(jié)點(diǎn)完成出隊(duì)\n", x);
		else {
			printf("當(dāng)前結(jié)點(diǎn)出隊(duì)失敗\n");
			break;
		}
	}
	if (EmptyQueue2(*Q)) 
		printf("當(dāng)前隊(duì)列為空隊(duì)列\(zhòng)n");
	else {
		printf("當(dāng)前隊(duì)列為非空隊(duì)列\(zhòng)n");
		return false;
	}
	return true;
}

同樣的為了避免隊(duì)列的銷毀出現(xiàn)問題,我們也是需要對不同的情況給予及時(shí)的反饋,以此來提高代碼的健壯性;

三、鏈隊(duì)列的實(shí)現(xiàn)演示

對于鏈隊(duì)列的基本操作我們已經(jīng)全部介紹完了,下面我們來看一下鏈隊(duì)列的實(shí)現(xiàn)過程;

3.1 帶頭結(jié)點(diǎn)的鏈隊(duì)列的實(shí)現(xiàn)演示

我們先來看一下完整的代碼:

//鏈隊(duì)列的數(shù)據(jù)類型
typedef int ElemType;
typedef struct LinkNode {
	ElemType data;//數(shù)據(jù)域
	struct LinkNode* next;//指針域
}LinkNode;//定義隊(duì)列中的每一個(gè)結(jié)點(diǎn)
typedef struct LinkQueue {
	LinkNode* front, * rear;//隊(duì)頭指針與隊(duì)尾指針
}LinkQueue;//定義隊(duì)列的隊(duì)頭指針與隊(duì)尾指針
//帶頭結(jié)點(diǎn)的鏈隊(duì)列的初始化
bool InitQueue(LinkQueue* Q) {
	if (!Q)
		return false;
	//為頭結(jié)點(diǎn)申請空間
	Q->rear = (LinkNode*)calloc(1, sizeof(LinkNode));
	if (!Q->rear)
		return false;
	//初始化頭結(jié)點(diǎn)
	Q->rear->next = NULL;
	 //初始化隊(duì)尾指針
	Q->front = Q->rear;//隊(duì)尾指針與隊(duì)頭指針同時(shí)指向頭結(jié)點(diǎn)
	return true;
}
//帶頭結(jié)點(diǎn)的鏈隊(duì)列的判空
bool EmptyQueue(LinkQueue Q) {
	if (Q.front == Q.rear)
		return true;
	return false;
}
//帶頭結(jié)點(diǎn)的鏈隊(duì)列的入隊(duì)
bool EnQueue(LinkQueue* Q, ElemType x) {
	if (!Q)
		return false;
	//為新的結(jié)點(diǎn)申請空間
	LinkNode* p = (LinkNode*)calloc(1, sizeof(LinkNode));
	if (!p)
		return false;
	p->data = x;//將數(shù)據(jù)存入新結(jié)點(diǎn)的數(shù)據(jù)域中
	p->next = Q->rear->next;//將新結(jié)點(diǎn)的指針域指向隊(duì)尾結(jié)點(diǎn)的后繼對象,也就是NULL
	//p->next = NULL;//新結(jié)點(diǎn)的指針域置為空
	Q->rear->next = p;//隊(duì)尾結(jié)點(diǎn)的后繼指針指向新結(jié)點(diǎn),完成插入操作
	Q->rear = p;//隊(duì)尾指針指向新的結(jié)點(diǎn)
	return true;
}
//帶頭結(jié)點(diǎn)的鏈隊(duì)列的出隊(duì)
bool DeQueue(LinkQueue* Q, ElemType* x) {
	if (!Q && !x)
		return false;
	if (EmptyQueue(*Q))
		return false;
	LinkNode* p = Q->front->next;//指向隊(duì)頭元素的指針
	Q->front->next = p->next;//修改頭結(jié)點(diǎn)的后繼結(jié)點(diǎn)
	if (Q->rear == p)//判斷此時(shí)的隊(duì)頭結(jié)點(diǎn)的后繼結(jié)點(diǎn)是否為隊(duì)尾結(jié)點(diǎn)
		Q->rear = Q->front;//當(dāng)為隊(duì)尾結(jié)點(diǎn)時(shí),移動(dòng)隊(duì)尾指針
	p->next = NULL;//隊(duì)頭元素結(jié)點(diǎn)完成出隊(duì)操作
	*x = p->data;//將出隊(duì)的元素通過變量x帶回到主函數(shù)中
	free(p);//釋放該結(jié)點(diǎn)占用的內(nèi)存空間
	p = NULL;//將指針置空防止出現(xiàn)野指針
	return true;
}
//帶頭結(jié)點(diǎn)的鏈隊(duì)列的查找
bool GetHead(LinkQueue Q, ElemType* x) {
	if (!x)
		return false;
	if (EmptyQueue(Q))
		return false;
	*x = Q.front->next->data;//通過頭結(jié)點(diǎn)找到隊(duì)頭元素的結(jié)點(diǎn)
	return true;
}
//帶頭結(jié)點(diǎn)的鏈隊(duì)列的銷毀
bool DestroyQueue(LinkQueue* Q) {
	if (!Q)
		return false;
	while (Q->front->next) {
		ElemType x = 0;
		if (DeQueue(Q, &x)) 
			printf("元素%d所在結(jié)點(diǎn)完成出隊(duì)\n", x);
		else {
			printf("當(dāng)前結(jié)點(diǎn)出隊(duì)失敗\n");
			break;//當(dāng)出隊(duì)失敗時(shí)退出循環(huán)
		}
	}
	if (EmptyQueue(*Q))//在結(jié)束循環(huán)后,對隊(duì)列進(jìn)行判空
		printf("當(dāng)前隊(duì)列為空隊(duì)列\(zhòng)n");
	else {
		printf("當(dāng)前隊(duì)列為非空隊(duì)列\(zhòng)n");
		return false;
	}
	free(Q->front);//釋放頭結(jié)點(diǎn)的內(nèi)存空間
	Q->rear = Q->front = NULL;//將隊(duì)頭指針與隊(duì)尾指針置為空,完成銷毀操作
	return true;
}
//帶頭結(jié)點(diǎn)的鏈隊(duì)列的實(shí)現(xiàn)
void test1() {
	//鏈隊(duì)列的創(chuàng)建
	LinkQueue Q;
	//鏈隊(duì)列的初始化
	if (InitQueue(&Q)) {
		printf("初始化成功\n");
	}
	else {
		printf("初始化失敗\n");
	}
	//鏈隊(duì)列的判空
	if (EmptyQueue(Q)) {
		printf("隊(duì)列為空隊(duì)列\(zhòng)n");
	}
	else {
		printf("隊(duì)列為非空隊(duì)列\(zhòng)n");
	}
	//鏈隊(duì)列的入隊(duì)
	int x = 0;
	while (scanf("%d", &x) == 1) {
		if (EnQueue(&Q, x)) {
			printf("元素%d成功入隊(duì)\n", x);
		}
		else {
			printf("元素%d入隊(duì)失敗\n", x);
		}
	}
	//鏈隊(duì)列的出隊(duì)
	if (DeQueue(&Q, &x)) {
		printf("元素%d成功出隊(duì)\n", x);
	}
	else {
		printf("元素出隊(duì)失敗\n");
		if (EmptyQueue(Q)) {
			printf("此時(shí)的隊(duì)列為空隊(duì)列\(zhòng)n");
		}
		else {
			printf("此時(shí)的隊(duì)列為非空隊(duì)列\(zhòng)n");
		}
	}
	//鏈隊(duì)列的查找
	if (GetHead(Q, &x)) {
		printf("此時(shí)的隊(duì)頭元素為%d\n", x);
	}
	else {
		printf("查找隊(duì)頭元素失敗\n");
		if (EmptyQueue(Q)) {
			printf("此時(shí)的隊(duì)列為空隊(duì)列\(zhòng)n");
		}
		else {
			printf("此時(shí)的隊(duì)列為非空隊(duì)列\(zhòng)n");
		}
	}
	//隊(duì)列的銷毀
	if (DestroyQueue(&Q)) {
		printf("隊(duì)列已成功銷毀\n");
	}
	else {
		printf("隊(duì)列銷毀失敗\n");
	}
}
int main() {
	test1();
	return 0;
}

接下來我們通過插入1~6這六個(gè)元素來驗(yàn)證每一步操作:
【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事,數(shù)據(jù)結(jié)構(gòu),保姆級教學(xué),數(shù)據(jù)結(jié)構(gòu),c語言,算法,改行學(xué)it
是可以看到所有的操作都能正常運(yùn)行,也就是說,此時(shí)我們很好的實(shí)現(xiàn)了帶頭結(jié)點(diǎn)的鏈隊(duì)列;

3.2 不帶頭結(jié)點(diǎn)的鏈隊(duì)列的實(shí)現(xiàn)演示

同樣我們還是先看完整的代碼:

//鏈隊(duì)列的數(shù)據(jù)類型
typedef int ElemType;
typedef struct LinkNode {
	ElemType data;//數(shù)據(jù)域
	struct LinkNode* next;//指針域
}LinkNode;//定義隊(duì)列中的每一個(gè)結(jié)點(diǎn)
typedef struct LinkQueue {
	LinkNode* front, * rear;//隊(duì)頭指針與隊(duì)尾指針
}LinkQueue;//定義隊(duì)列的隊(duì)頭指針與隊(duì)尾指針
//不帶頭結(jié)點(diǎn)的鏈隊(duì)列的初始化
bool InitQueue2(LinkQueue* Q) {
	if (!Q)
		return false;
	//初始化隊(duì)頭指針與隊(duì)尾指針
	Q->front = Q->rear = NULL;
	return true;
}
//帶頭結(jié)點(diǎn)的鏈隊(duì)列的判空
bool EmptyQueue2(LinkQueue Q) {
	//通過隊(duì)頭指針判斷
	if (Q.front == NULL)//可以簡寫成if(!Q.front)
		return true;
	//或者通過隊(duì)尾指針判斷
	//if (Q.rear == NULL)//可以簡寫成if(!Q.rear)
	//	return true;
	return false;
}
//不帶頭結(jié)點(diǎn)的鏈隊(duì)列的入隊(duì)
bool EnQueue2(LinkQueue* Q, ElemType x) {
	if (!Q)
		return false;
	if (EmptyQueue2(*Q)) {
		Q->rear = (LinkNode*)calloc(1, sizeof(LinkNode));
		if (!Q->rear)
			return false;
		Q->rear->data = x;//將數(shù)據(jù)存入新結(jié)點(diǎn)中
		Q->rear->next = NULL;//將新結(jié)點(diǎn)的后繼指針置為空
		Q->front = Q->rear;//將隊(duì)頭指針指向新結(jié)點(diǎn)
	}
	else {
		LinkNode* p = (LinkNode*)calloc(1, sizeof(LinkNode));
		if (!p)
			return false;
		p->data = x;//將數(shù)據(jù)存入新結(jié)點(diǎn)中
		p->next = Q->rear->next;//將新結(jié)點(diǎn)的后繼指針指向隊(duì)尾結(jié)點(diǎn)的后繼對象
		Q->rear->next = p;//隊(duì)尾結(jié)點(diǎn)的后繼指針指向新結(jié)點(diǎn)完成插入操作
		Q->rear = p;//隊(duì)尾結(jié)點(diǎn)指向新結(jié)點(diǎn)
	}
	return true;
}
//不帶頭結(jié)點(diǎn)的鏈隊(duì)列的出隊(duì)
bool DeQueue2(LinkQueue* Q, ElemType* x) {
	if (!Q && !x)
		return false;
	if (EmptyQueue2(*Q))
		return false;
	LinkNode* p = Q->front;//指向隊(duì)頭結(jié)點(diǎn)的指針
	Q->front = p->next;//隊(duì)頭指針指向隊(duì)頭結(jié)點(diǎn)的后繼結(jié)點(diǎn)
	if (Q->rear == p)//判斷此時(shí)的隊(duì)頭結(jié)點(diǎn)是否為隊(duì)尾結(jié)點(diǎn)
		Q->rear = NULL;//當(dāng)為隊(duì)尾結(jié)點(diǎn)時(shí),需要移動(dòng)隊(duì)尾指針
	p->next = NULL;//隊(duì)頭結(jié)點(diǎn)完成出隊(duì)操作
	*x = p->data;//將出隊(duì)的元素帶回主函數(shù)中
	free(p);//釋放隊(duì)頭結(jié)點(diǎn)的內(nèi)存空間
	p = NULL;//將指針p置為空,防止出現(xiàn)野指針
	return true;
}
//不帶頭結(jié)點(diǎn)的鏈隊(duì)列的查找
bool GetHead2(LinkQueue Q, ElemType* x) {
	if (!x)
		return false;
	if (EmptyQueue2(Q))
		return false;
	*x = Q.front->data;//通過隊(duì)頭指針訪問隊(duì)頭結(jié)點(diǎn)
	return true;
}
//不帶頭結(jié)點(diǎn)的鏈隊(duì)列的銷毀
bool DestroyQueue2(LinkQueue* Q) {
	if (!Q)
		return false;
	while (Q->front) {
		ElemType x = 0;
		if (DeQueue2(Q, &x)) 
			printf("元素%d所在結(jié)點(diǎn)完成出隊(duì)\n", x);
		else {
			printf("當(dāng)前結(jié)點(diǎn)出隊(duì)失敗\n");
			break;
		}
	}
	if (EmptyQueue2(*Q)) 
		printf("當(dāng)前隊(duì)列為空隊(duì)列\(zhòng)n");
	else {
		printf("當(dāng)前隊(duì)列為非空隊(duì)列\(zhòng)n");
		return false;
	}
	return true;
}
//不帶頭結(jié)點(diǎn)的鏈隊(duì)列的實(shí)現(xiàn)
void test2() {
	//鏈隊(duì)列的創(chuàng)建
	LinkQueue Q;
	//鏈隊(duì)列的初始化
	if (InitQueue2(&Q)) {
		printf("初始化成功\n");
	}
	else {
		printf("初始化失敗\n");
	}
	//鏈隊(duì)列的判空
	if (EmptyQueue2(Q)) {
		printf("隊(duì)列為空隊(duì)列\(zhòng)n");
	}
	else {
		printf("隊(duì)列為非空隊(duì)列\(zhòng)n");
	}
	//鏈隊(duì)列的入隊(duì)
	int x = 0;
	while (scanf("%d", &x) == 1) {
		if (EnQueue2(&Q, x)) {
			printf("元素%d成功入隊(duì)\n", x);
		}
		else {
			printf("元素%d入隊(duì)失敗\n", x);
		}
	}
	//鏈隊(duì)列的出隊(duì)
	if (DeQueue2(&Q, &x)) {
		printf("元素%d成功出隊(duì)\n", x);
	}
	else {
		printf("元素出隊(duì)失敗\n");
		if (EmptyQueue2(Q)) {
			printf("此時(shí)的隊(duì)列為空隊(duì)列\(zhòng)n");
		}
		else {
			printf("此時(shí)的隊(duì)列為非空隊(duì)列\(zhòng)n");
		}
	}
	//鏈隊(duì)列的查找
	if (GetHead2(Q, &x)) {
		printf("此時(shí)的隊(duì)頭元素為%d\n", x);
	}
	else {
		printf("查找隊(duì)頭元素失敗\n");
		if (EmptyQueue2(Q)) {
			printf("此時(shí)的隊(duì)列為空隊(duì)列\(zhòng)n");
		}
		else {
			printf("此時(shí)的隊(duì)列為非空隊(duì)列\(zhòng)n");
		}
	}
	//隊(duì)列的銷毀
	if (DestroyQueue2(&Q)) {
		printf("隊(duì)列已成功銷毀\n");
	}
	else {
		printf("隊(duì)列銷毀失敗\n");
	}
}
int main() {
	test2();
	return 0;
}

下面我們來看一下測試結(jié)果:
【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事,數(shù)據(jù)結(jié)構(gòu),保姆級教學(xué),數(shù)據(jù)結(jié)構(gòu),c語言,算法,改行學(xué)it
可以看到,此時(shí)我們也很好的實(shí)現(xiàn)了不帶頭結(jié)點(diǎn)的鏈隊(duì)列的各個(gè)操作。

結(jié)語

在今天的內(nèi)容中,我們詳細(xì)介紹了兩種鏈隊(duì)列及其基本操作的實(shí)現(xiàn)與演示。在介紹基本操作實(shí)現(xiàn)的過程中,也有將大家容易忽視的問題進(jìn)行了介紹,比如

  1. 數(shù)據(jù)類型的定義為什么是分兩次進(jìn)行定義?
  2. 在進(jìn)行鏈隊(duì)列的初始化時(shí),為什么不能直接通過鏈隊(duì)列Q來進(jìn)行初始化?
  3. 在定義頭結(jié)點(diǎn)時(shí)為什么不能通過隊(duì)頭指針來申請空間?

等等,如果大家對鏈隊(duì)列的基本操作與實(shí)現(xiàn)還有不理解的地方可以仔細(xì)的閱讀本文,并做下記錄,如果還是有不清楚的點(diǎn),可以評論區(qū)留言或者私信博主,博主看到后會對相應(yīng)的問題進(jìn)行解答。

今天的內(nèi)容到這里就全部介紹了,希望今天的內(nèi)容對大家理解鏈隊(duì)列及其基本操作的實(shí)現(xiàn)上能提供一點(diǎn)幫助,在下一篇內(nèi)容中,我們將介紹雙端隊(duì)列的相關(guān)知識點(diǎn),大家記得關(guān)注哦?。?!

最后感謝各位的翻閱,咱們下一篇再見?。。?span toymoban-style="hidden">文章來源地址http://www.zghlxwxcb.cn/news/detail-820970.html

到了這里,關(guān)于【數(shù)據(jù)結(jié)構(gòu)】在鏈隊(duì)列中你可能忽視的二三事的文章就介紹完了。如果您還想了解更多內(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)—循環(huán)隊(duì)列(環(huán)形隊(duì)列)

    數(shù)據(jù)結(jié)構(gòu)—循環(huán)隊(duì)列(環(huán)形隊(duì)列)

    循環(huán)隊(duì)列是一種線性數(shù)據(jù)結(jié)構(gòu),其操作表現(xiàn)基于 FIFO(先進(jìn)先出)原則并且 隊(duì)尾被連接在隊(duì)首之后以形成一個(gè)循環(huán) 。它也被稱為“ 環(huán)形緩沖器 ”。 循環(huán)隊(duì)列的一個(gè)好處是可以利用這個(gè)隊(duì)列之前用過的空間。在一個(gè)普通隊(duì)列里,一旦一個(gè)隊(duì)列滿了,我們就不能插入下一個(gè)元素

    2024年02月11日
    瀏覽(19)
  • 【數(shù)據(jù)結(jié)構(gòu)-隊(duì)列】雙端隊(duì)列

    【數(shù)據(jù)結(jié)構(gòu)-隊(duì)列】雙端隊(duì)列

    ??????歡迎來到我的博客,很高興能夠在這里和您見面!希望您在這里可以感受到一份輕松愉快的氛圍,不僅可以獲得有趣的內(nèi)容和知識,也可以暢所欲言、分享您的想法和見解。 推薦:kuan 的首頁,持續(xù)學(xué)習(xí),不斷總結(jié),共同進(jìn)步,活到老學(xué)到老 導(dǎo)航 檀越劍指大廠系列:全面總

    2024年02月09日
    瀏覽(27)
  • 數(shù)據(jù)結(jié)構(gòu)--隊(duì)列與循環(huán)隊(duì)列

    數(shù)據(jù)結(jié)構(gòu)--隊(duì)列與循環(huán)隊(duì)列

    ? ? ? ? 隊(duì)列是什么,先聯(lián)想一下隊(duì),排隊(duì)先來的人排前面先出,后來的人排后面后出;隊(duì)列的性質(zhì)也一樣,先進(jìn)隊(duì)列的數(shù)據(jù)先出,后進(jìn)隊(duì)列的后出;就像圖一的樣子: ?圖1 ? ? ? ? 如圖1,1號元素是最先進(jìn)的,開始出隊(duì)時(shí),那么他就是最先出的,然后12進(jìn)隊(duì),就應(yīng)該排在最

    2024年02月10日
    瀏覽(15)
  • 探索Python數(shù)據(jù)結(jié)構(gòu)與算法:解鎖編程的無限可能

    探索Python數(shù)據(jù)結(jié)構(gòu)與算法:解鎖編程的無限可能

    重溫Python,適合新手搭建知識體系,也適合大佬的溫故知新~ 由于涉及到算法,知識深度非常深,本文只講表層來重溫記憶,想要深入需要自行多加了解和刷題哈 1.1 數(shù)據(jù)結(jié)構(gòu)與算法對于編程的重要性 重要性 : 提高程序效率 :優(yōu)秀的數(shù)據(jù)結(jié)構(gòu)和算法可以顯著提高程序的執(zhí)行

    2024年01月17日
    瀏覽(22)
  • 數(shù)據(jù)結(jié)構(gòu),隊(duì)列,順序表隊(duì)列,鏈表隊(duì)列

    ????????隊(duì)列是一種常見的數(shù)據(jù)結(jié)構(gòu),它具有先進(jìn)先出(First-In-First-Out,F(xiàn)IFO)的特性,類似于排隊(duì)等候的場景。以下是隊(duì)列的要點(diǎn): 1. 定義:隊(duì)列是一種線性數(shù)據(jù)結(jié)構(gòu),由一系列元素組成,可以進(jìn)行插入和刪除操作。插入操作(稱為入隊(duì))只能在隊(duì)列的末尾進(jìn)行,刪除操

    2024年02月11日
    瀏覽(18)
  • 【數(shù)據(jù)結(jié)構(gòu)】棧和隊(duì)列(隊(duì)列篇)

    【數(shù)據(jù)結(jié)構(gòu)】棧和隊(duì)列(隊(duì)列篇)

    上期我們已經(jīng)學(xué)習(xí)了數(shù)據(jù)結(jié)構(gòu)中的棧,這期我們開始學(xué)習(xí)隊(duì)列。 目錄 1.隊(duì)列的概念及結(jié)構(gòu) 2.隊(duì)列的實(shí)現(xiàn) 隊(duì)列結(jié)構(gòu)體定義 常用接口函數(shù) 初始化隊(duì)列 隊(duì)尾入隊(duì)列 隊(duì)頭出隊(duì)列 獲取隊(duì)列頭部元素、 獲取隊(duì)列隊(duì)尾元素 獲取隊(duì)列中有效元素個(gè)數(shù) 檢測隊(duì)列是否為空 銷毀隊(duì)列 3.循環(huán)隊(duì)

    2024年02月13日
    瀏覽(23)
  • 數(shù)據(jù)結(jié)構(gòu)——線性數(shù)據(jù)結(jié)構(gòu)(數(shù)組,鏈表,棧,隊(duì)列)

    數(shù)據(jù)結(jié)構(gòu)——線性數(shù)據(jù)結(jié)構(gòu)(數(shù)組,鏈表,棧,隊(duì)列)

    數(shù)組(Array) 是一種很常見的數(shù)據(jù)結(jié)構(gòu)。它由相同類型的元素(element)組成,并且是使用一塊連續(xù)的內(nèi)存來存儲。 我們直接可以利用元素的索引(index)可以計(jì)算出該元素對應(yīng)的存儲地址。 數(shù)組的特點(diǎn)是: 提供隨機(jī)訪問 并且容量有限。 2.1. 鏈表簡介 鏈表(LinkedList) 雖然是

    2024年02月11日
    瀏覽(21)
  • 【數(shù)據(jù)結(jié)構(gòu)】從數(shù)據(jù)結(jié)構(gòu)角度深入探究隊(duì)列

    【數(shù)據(jù)結(jié)構(gòu)】從數(shù)據(jù)結(jié)構(gòu)角度深入探究隊(duì)列

    隊(duì)列是計(jì)算機(jī)科學(xué)中的一種基本數(shù)據(jù)結(jié)構(gòu),用于存儲和管理數(shù)據(jù)。在計(jì)算機(jī)程序中,隊(duì)列被廣泛應(yīng)用于任務(wù)調(diào)度、進(jìn)程管理等場景。本文將介紹隊(duì)列的概念、特點(diǎn)、常見操作以及應(yīng)用。 你們在用電腦時(shí)有沒有經(jīng)歷過,機(jī)器有時(shí)會處于疑似死機(jī)的狀態(tài),鼠標(biāo)點(diǎn)什么似乎都沒用,

    2024年02月06日
    瀏覽(26)
  • 【數(shù)據(jù)結(jié)構(gòu)】棧和隊(duì)列(鏈表模擬隊(duì)列)

    【數(shù)據(jù)結(jié)構(gòu)】棧和隊(duì)列(鏈表模擬隊(duì)列)

    ? 學(xué)習(xí)本章節(jié)必須具備 單鏈表的前置知識, 建議提前學(xué)習(xí):點(diǎn)擊鏈接學(xué)習(xí):單鏈表各種功能函數(shù) 細(xì)節(jié) 詳解 本章節(jié)是學(xué)習(xí)用 單鏈表模擬隊(duì)列 1. 單鏈表實(shí)現(xiàn)隊(duì)列 思路如下 隊(duì)列:只允許在一端進(jìn)行插入數(shù)據(jù)操作,在另一端進(jìn)行刪除數(shù)據(jù)操作的特殊線性表,隊(duì)列具有先 進(jìn)先出

    2024年04月27日
    瀏覽(23)
  • 數(shù)據(jù)結(jié)構(gòu)--隊(duì)列2--雙端隊(duì)列--java雙端隊(duì)列

    數(shù)據(jù)結(jié)構(gòu)--隊(duì)列2--雙端隊(duì)列--java雙端隊(duì)列

    雙端隊(duì)列,和前面學(xué)的隊(duì)列和棧的區(qū)別在于雙端隊(duì)列2端都可以進(jìn)行增刪,其他2個(gè)都是只能一端可以增/刪。 因?yàn)?端都需要可以操作所以我們使用雙向鏈表 我們也需要一共頭節(jié)點(diǎn) 所以節(jié)點(diǎn)設(shè)置 隊(duì)列的類維護(hù): 容量 隊(duì)列元素個(gè)數(shù) 哨兵 哨兵初始頭和尾都指向自己,value為null

    2024年02月10日
    瀏覽(22)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包