??個(gè)人主頁(yè)??
?個(gè)人專欄——數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)?
??點(diǎn)擊關(guān)注??一起學(xué)習(xí)C語(yǔ)言????
導(dǎo)讀:
我們?cè)谇懊鎸W(xué)習(xí)了單鏈表和順序表,今天我們來(lái)學(xué)習(xí)棧和隊(duì)列。
棧和隊(duì)列相對(duì)于單鏈表和順序表來(lái)說(shuō)是較為簡(jiǎn)單的,之后我們?cè)偕钊雽W(xué)習(xí)雙鏈表。關(guān)注博主或是訂閱專欄,掌握第一消息。
一、 棧
1. 棧的概念及結(jié)構(gòu)
棧(Stack)是一種只能在一端進(jìn)行插入和刪除操作的線性數(shù)據(jù)結(jié)構(gòu),該端被稱為棧頂(Top),另一端被稱為棧底(Bottom)。
棧的特點(diǎn)是后進(jìn)先出(Last In First Out, LIFO),即最后放入棧中的元素最先被彈出。棧中的元素可以是任意類型,但棧頂永遠(yuǎn)只能是一個(gè)元素。
壓棧:棧的插入操作叫做進(jìn)棧/壓棧/入棧,入數(shù)據(jù)在棧頂。
出棧:棧的刪除操作叫做出棧。出數(shù)據(jù)也在棧頂。
2. 棧的實(shí)現(xiàn)
棧可以用數(shù)組或鏈表來(lái)實(shí)現(xiàn),常見應(yīng)用場(chǎng)景包括函數(shù)調(diào)用、表達(dá)式求值、括號(hào)匹配、逆序輸出等。
相對(duì)而言數(shù)組的結(jié)構(gòu)實(shí)現(xiàn)更優(yōu)一些。因?yàn)閿?shù)組在尾上插入數(shù)據(jù)的 代價(jià)比較小。
其基本操作包括:
push(x): 將元素x壓入棧頂。
pop(): 彈出棧頂元素并返回其值。
top(): 返回棧頂元素的值,但不彈出。
empty():判斷棧是否為空。
3. 實(shí)現(xiàn)代碼
我們需要?jiǎng)?chuàng)建兩個(gè) C文件: study.c 和 Stack.c,以及一個(gè) 頭文件: Stack.h。
頭文件來(lái)聲明函數(shù),一個(gè)C文件來(lái)定義函數(shù),另外一個(gè)C文件來(lái)用于主函數(shù)main()進(jìn)行測(cè)試。
3.1 定義結(jié)構(gòu)體
typedef是類型定義的意思。typedef struct 是為了使用這個(gè)結(jié)構(gòu)體方便。
若struct Stack {}這樣來(lái)定義結(jié)構(gòu)體的話。在申請(qǐng)Stack 的變量時(shí),需要這樣寫,struct Stack n;
若用typedef,可以這樣寫,typedef struct Stack{}ST; 。在申請(qǐng)變量時(shí)就可以這樣寫,ST n;
區(qū)別就在于使用時(shí),是否可以省去struct這個(gè)關(guān)鍵字。
Stack.h
typedef struct Stack
{
STDataType* a;
int top; //標(biāo)識(shí)棧頂?shù)奈恢?/span>
int capacity;
}ST;
3.2 初始化棧
Stack.h 聲明函數(shù)
// 初始化棧
void STInit(ST* pst);
Stack.c 定義函數(shù)
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
3.3 銷毀棧
動(dòng)態(tài)內(nèi)存空間開辟,使用完之后需要進(jìn)行銷毀。
Stack.h 聲明函數(shù)
// 銷毀
void STDestroy(ST* pst);
Stack.c 定義函數(shù)
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = 0;
}
3.4 入棧
我們?cè)陧樞虮砗蛦蜗蜴湵砝?,都另定義一個(gè)函數(shù)來(lái)進(jìn)行空間的開辟,這樣我們?cè)谄渌暮瘮?shù)中有開辟空間的需要只用調(diào)用即可,而無(wú)需再去寫一次開辟空間的代碼。但是在棧中我們只有在入棧的函數(shù)中需要進(jìn)行空間的開辟,所有不用再單獨(dú)寫一個(gè)函數(shù)。
Stack.h 聲明函數(shù)
// 入棧
void STPush(ST* pst, STDataType x);
Stack.c 定義函數(shù)
void STPush(ST* pst, STDataType x)
{
assert(pst);
// 檢查空間,如果滿了,進(jìn)行增容
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
//如果開辟成功則重新賦給原來(lái)的數(shù)組指針
pst->a = tmp;
pst->capacity = newcapacity;
}
//棧頂從0開始,可以作為數(shù)組的下標(biāo)來(lái)進(jìn)行插入數(shù)據(jù)
pst->a[pst->top] = x;
pst->top++;
}
3.5 出棧
后進(jìn)先出原則,最后進(jìn)來(lái)的數(shù)據(jù)先出。
Stack.h 聲明函數(shù)
// 出棧
void STPop(ST* pst);
Stack.c 定義函數(shù)
// 出棧
void STPop(ST* pst)
{
assert(pst);
//top大于0,棧里面有數(shù)據(jù)才能刪除數(shù)據(jù)
assert(pst->top > 0);
//直接讓top--,不讓訪問(wèn)即可
pst->top--;
}
3.6 獲取棧頂元素
棧并不能像打印數(shù)組那樣把數(shù)據(jù)全部打印出來(lái),只能獲取到棧頂?shù)脑兀胍@取其它數(shù)據(jù)就只能先把其它的數(shù)據(jù)給刪除,也就是出棧。
Stack.h 聲明函數(shù)
// 獲取棧頂元素
STDataType STTop(ST* pst);
Stack.c 定義函數(shù)
// 獲取棧頂元素
STDataType STTop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
//top-1即是棧頂元素
return pst->a[pst->top - 1];
}
3.7 檢測(cè)棧是否為空
Stack.h 聲明函數(shù)
// 檢測(cè)棧是否為空,如果為空返回true,如果不為空返回false
bool STEmpty(ST* pst);
Stack.c 定義函數(shù)
// 檢測(cè)棧是否為空,如果為空返回true,如果不為空返回false
bool STEmpty(ST* pst)
{
assert(pst);
//如果表達(dá)式成立則為真
return pst->top == 0;
}
3.8 獲取棧中有效元素個(gè)數(shù)
Stack.h 聲明函數(shù)
// 獲取棧中有效元素個(gè)數(shù)
int STSize(ST* pst);
Stack.c 定義函數(shù)
//獲取棧中有效元素個(gè)數(shù)
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
4. 代碼整理
4.1 Stack.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top; //標(biāo)識(shí)棧頂?shù)奈恢?/span>
int capacity;
}ST;
// 初始化棧
void STInit(ST* pst);
// 銷毀
void STDestroy(ST* pst);
// 入棧
void STPush(ST* pst, STDataType x);
// 出棧
void STPop(ST* pst);
// 獲取棧頂元素
STDataType STTop(ST* pst);
// 檢測(cè)棧是否為空,如果為空返回true,如果不為空返回false
bool STEmpty(ST* pst);
// 獲取棧中有效元素個(gè)數(shù)
int STSize(ST* pst);
4.2 Stack.c
#include "Stack.h"
//初始化棧
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
// 銷毀棧
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = 0;
}
// 入棧
void STPush(ST* pst, STDataType x)
{
assert(pst);
// 檢查空間,如果滿了,進(jìn)行增容
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
//如果開辟成功則重新賦給原來(lái)的數(shù)組指針
pst->a = tmp;
pst->capacity = newcapacity;
}
//棧頂從0開始,可以作為數(shù)組的下標(biāo)來(lái)進(jìn)行插入數(shù)據(jù)
pst->a[pst->top] = x;
pst->top++;
}
// 出棧
void STPop(ST* pst)
{
assert(pst);
//top大于0,棧里面有數(shù)據(jù)才能刪除數(shù)據(jù)
assert(pst->top > 0);
//直接讓top--,有效數(shù)據(jù)減一即可
pst->top--;
}
// 獲取棧頂元素
STDataType STTop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
//top-1即是棧頂元素
return pst->a[pst->top - 1];
}
// 檢測(cè)棧是否為空,如果為空返回true,如果不為空返回false
bool STEmpty(ST* pst)
{
assert(pst);
//如果表達(dá)式成立則為真
return pst->top == 0;
}
//獲取棧中有效元素個(gè)數(shù)
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
4.3 study.c
#include "Stack.h"
void TestST1()
{
ST s;
STInit(&s);
STPush(&s, 1);
STPush(&s, 2);
STPush(&s, 3);
STPush(&s, 4);
STPush(&s, 5);
printf("%d\n", STTop(&s));
//一 對(duì) 多
//入棧順序 -- 出棧順序
while (!STEmpty(&s))
{
printf("%d ", STTop(&s));
STPop(&s);
}
printf("\n");
STDestroy(&s);
}
int main()
{
TestST1();
return 0;
}
二、隊(duì)列
1. 隊(duì)列的概念及結(jié)構(gòu)
隊(duì)列是一種線性的數(shù)據(jù)結(jié)構(gòu),它可以存儲(chǔ)一系列數(shù)據(jù),其中第一個(gè)添加的數(shù)據(jù)會(huì)被第一個(gè)刪除,也就是先進(jìn)先出(FIFO)的原則。
只允許在一端進(jìn)行插入數(shù)據(jù)操作,在另一端進(jìn)行刪除數(shù)據(jù)操作的特殊線性表,隊(duì)列具有先進(jìn)先出
FIFO(First In First Out) 入隊(duì)列:進(jìn)行插入操作的一端稱為隊(duì)尾 出隊(duì)列:進(jìn)行刪除操作的一端稱為隊(duì)頭
2. 隊(duì)列的實(shí)現(xiàn)
隊(duì)列也可以數(shù)組和鏈表的結(jié)構(gòu)實(shí)現(xiàn),使用鏈表的結(jié)構(gòu)實(shí)現(xiàn)更優(yōu)一些,因?yàn)槿绻褂脭?shù)組的結(jié)構(gòu),出隊(duì)列在數(shù)組頭上出數(shù)據(jù),效率會(huì)比較低。
隊(duì)列通常有兩個(gè)指針,一個(gè)是front指針,指向隊(duì)列的第一個(gè)元素,另一個(gè)是rear指針,指向隊(duì)列的最后一個(gè)元素。當(dāng)一個(gè)新元素進(jìn)入隊(duì)列時(shí),它被插入到rear指針?biāo)赶虻奈恢?,?dāng)一個(gè)元素從隊(duì)列中刪除時(shí),它會(huì)從front指針?biāo)赶虻奈恢帽粍h除。
3. 實(shí)現(xiàn)代碼
我們需要?jiǎng)?chuàng)建兩個(gè) C文件: study.c 和 Queue.c,以及一個(gè) 頭文件: Queue.h。
頭文件來(lái)聲明函數(shù),一個(gè)C文件來(lái)定義函數(shù),另外一個(gè)C文件來(lái)用于主函數(shù)main()進(jìn)行測(cè)試。
3.1 定義結(jié)構(gòu)體
定義了一個(gè)鏈?zhǔn)疥?duì)列的數(shù)據(jù)結(jié)構(gòu)。
包含了兩個(gè)結(jié)構(gòu)體:
-
QNode結(jié)構(gòu)體表示隊(duì)列中的一個(gè)節(jié)點(diǎn),包含一個(gè)整數(shù)數(shù)據(jù)成員val和指向下一個(gè)節(jié)點(diǎn)的指針next。
-
Queue結(jié)構(gòu)體表示一個(gè)隊(duì)列,包含指向隊(duì)頭和隊(duì)尾節(jié)點(diǎn)的指針phead和ptail,以及隊(duì)列的大小size。
這個(gè)隊(duì)列是通過(guò)鏈?zhǔn)浇Y(jié)構(gòu)實(shí)現(xiàn)的,即每個(gè)節(jié)點(diǎn)都包含一個(gè)指向下一個(gè)節(jié)點(diǎn)的指針。隊(duì)列的頭指針phead指向隊(duì)列的第一個(gè)節(jié)點(diǎn),而隊(duì)列的尾指針ptail指向隊(duì)列的最后一個(gè)節(jié)點(diǎn)。
鏈?zhǔn)疥?duì)列的優(yōu)點(diǎn)是可以動(dòng)態(tài)地增加和減少隊(duì)列的大小,而不需要像順序隊(duì)列那樣預(yù)留一定的空間。缺點(diǎn)是每個(gè)節(jié)點(diǎn)都需要額外的指針空間來(lái)指向下一個(gè)節(jié)點(diǎn),因此相對(duì)于順序隊(duì)列會(huì)占用更多的存儲(chǔ)空間。
Queue.h
// 鏈?zhǔn)浇Y(jié)構(gòu):表示隊(duì)列
typedef int QDataType;
typedef struct QueueNode
{
QDataType val;
struct QueueNode* next;
}QNode;
// 隊(duì)列的結(jié)構(gòu)
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
}Queue;
3.2 初始化隊(duì)列
Queue.h
// 初始化隊(duì)列
void QueueInit(Queue* pq);
Queue.c
void QueueInit(Queue* pq)
{
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
3.3 銷毀隊(duì)列
Queue.h
// 銷毀隊(duì)列
void QueueDestroy(Queue* pq);
Queue.c
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead->next;
while (cur)
{
free(pq->phead);
pq->phead = cur;
cur = cur->next;
}
cur = NULL;
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
3.4 隊(duì)尾入隊(duì)列
Queue.h
// 隊(duì)尾入隊(duì)列
void QueuePush(Queue* pq, QDataType x);
Queue.c
void QueuePush(Queue* pq, QDataType x)
{
//開辟新空間
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->val = x;
newnode->next = NULL;
if (pq->ptail == NULL)
{
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
3.5 隊(duì)頭出隊(duì)列
Queue.h
// 隊(duì)頭出隊(duì)列
void QueuePop(Queue* pq);
Queue.c
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->phead);
QNode* tmp = pq->phead;
pq->phead = pq->phead->next;
free(tmp);
tmp = NULL;
if (pq->phead == NULL)
{
pq->ptail = NULL;
}
pq->size--;
}
3. 6 獲取隊(duì)列頭部元素
Queue.h
// 獲取隊(duì)列頭部元素
QDataType QueueFront(Queue* pq);
Queue.c
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->phead);
return pq->phead->val;
}
3.7 獲取隊(duì)列隊(duì)尾元素
Queue.h
// 獲取隊(duì)列隊(duì)尾元素
QDataType QueueBack(Queue* pq);
Queue.c
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->ptail);
return pq->ptail->val;
}
3.8 檢測(cè)隊(duì)列是否為空
Queue.h
// 檢測(cè)隊(duì)列是否為空,如果為空返回true,如果非空返回false
bool QueueEmpty(Queue* pq);
Queue.c
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
3.9 獲取隊(duì)列中有效元素個(gè)數(shù)
Queue.h文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-752591.html
// 獲取隊(duì)列中有效元素個(gè)數(shù)
int QueueSize(Queue* pq);
Queue.c文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-752591.html
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
4. 代碼整理
4.1 Queue.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
// 鏈?zhǔn)浇Y(jié)構(gòu):表示隊(duì)列
typedef int QDataType;
typedef struct QueueNode
{
QDataType val;
struct QueueNode* next;
}QNode;
// 隊(duì)列的結(jié)構(gòu)
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
}Queue;
// 初始化隊(duì)列
void QueueInit(Queue* pq);
// 銷毀隊(duì)列
void QueueDestroy(Queue* pq);
// 隊(duì)尾入隊(duì)列
void QueuePush(Queue* pq, QDataType x);
// 隊(duì)頭出隊(duì)列
void QueuePop(Queue* pq);
// 獲取隊(duì)列頭部元素
QDataType QueueFront(Queue* pq);
// 獲取隊(duì)列隊(duì)尾元素
QDataType QueueBack(Queue* pq);
// 檢測(cè)隊(duì)列是否為空,如果為空返回true,如果非空返回false
bool QueueEmpty(Queue* pq);
// 獲取隊(duì)列中有效元素個(gè)數(shù)
int QueueSize(Queue* pq);
4.2 Queue.c
#include "Queue.h"
// 初始化隊(duì)列
void QueueInit(Queue* pq)
{
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
// 銷毀隊(duì)列
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead->next;
while (cur)
{
free(pq->phead);
pq->phead = cur;
cur = cur->next;
}
cur = NULL;
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
// 隊(duì)尾入隊(duì)列
void QueuePush(Queue* pq, QDataType x)
{
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->val = x;
newnode->next = NULL;
if (pq->ptail == NULL)
{
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
// 隊(duì)頭出隊(duì)列
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->phead);
QNode* tmp = pq->phead;
pq->phead = pq->phead->next;
free(tmp);
tmp = NULL;
if (pq->phead == NULL)
{
pq->ptail = NULL;
}
pq->size--;
}
// 獲取隊(duì)列頭部元素
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->phead);
return pq->phead->val;
}
// 獲取隊(duì)列隊(duì)尾元素
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->ptail);
return pq->ptail->val;
}
// 檢測(cè)隊(duì)列是否為空,如果為空返回true,如果非空返回false
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
// 獲取隊(duì)列中有效元素個(gè)數(shù)
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
4.3 study.c
#include "Queue.h"
void TestQ1()
{
Queue s;
QueueInit(&s);
QueuePush(&s, 1);
QueuePush(&s, 2);
QueuePush(&s, 3);
QueuePush(&s, 4);
QueuePush(&s, 5);
printf("%d ", QueueFront(&s));
printf("%d ", QueueBack(&s));
printf("%d\n", QueueSize(&s));
QueuePop(&s);
QueuePop(&s);
printf("%d ", QueueFront(&s));
printf("%d\n", QueueSize(&s));
if (!QueueEmpty(&s))
{
QueuePop(&s);
printf("%d ", QueueFront(&s));
printf("%d\n", QueueSize(&s));
}
QueueDestroy(&s);
printf("%d\n", QueueSize(&s));
}
int main()
{
TestQ1();
return 0;
}
到了這里,關(guān)于【C語(yǔ)言】數(shù)據(jù)結(jié)構(gòu)——棧和隊(duì)列實(shí)例探究的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!