?
學(xué)習(xí)本章節(jié)必須具備 單鏈表的前置知識(shí),
建議提前學(xué)習(xí):點(diǎn)擊鏈接學(xué)習(xí):?jiǎn)捂湵砀鞣N功能函數(shù) 細(xì)節(jié) 詳解
本章節(jié)是學(xué)習(xí)用 單鏈表模擬隊(duì)列
1. 單鏈表實(shí)現(xiàn)隊(duì)列 思路如下
隊(duì)列:只允許在一端進(jìn)行插入數(shù)據(jù)操作,在另一端進(jìn)行刪除數(shù)據(jù)操作的特殊線性表,隊(duì)列具有先 進(jìn)先出FIFO(First In First Out) 入隊(duì)列:進(jìn)行插入操作的一端稱(chēng)為隊(duì)尾 出隊(duì)列:進(jìn)行刪除操作的一 端稱(chēng)為隊(duì)頭
1.1 使用 數(shù)組 還是 鏈表 模擬 隊(duì)列 結(jié)構(gòu)?
因?yàn)樾枰?模擬隊(duì)列 先進(jìn)先出的 特性:隊(duì)頭 只能出,隊(duì)尾 只能進(jìn)
若 使用 數(shù)組模擬,每次 pop 隊(duì)頭操作,就需要 全部元素向前面移動(dòng),時(shí)間復(fù)雜度為 O(n)
綜上,因?yàn)樾枰?考慮位置變化,選擇 鏈表 實(shí)現(xiàn) 隊(duì)列 較優(yōu)
1.2. 需要使用 什么類(lèi)型的 鏈表模擬隊(duì)列?
單向
帶頭 / 不帶頭 都可以 :因?yàn)樯诒恢饕糜?雙向鏈表 找尾 ,為了方便刪除,這里差別不大
不循環(huán)
我們下面實(shí)現(xiàn)的 是 單向不帶頭不循環(huán)鏈表
實(shí)際上,單向或雙向,循環(huán)或不循環(huán),帶頭或不帶頭 完全取決于 你自己要實(shí)現(xiàn)一個(gè)功能的需求,不是說(shuō) 一定要固定使用 哪一個(gè)套 ,需要靈活選擇使用
1.3. 單向鏈表 實(shí)現(xiàn)隊(duì)列 的鏈表節(jié)點(diǎn)結(jié)構(gòu)體創(chuàng)建:
typedef int QDataType;
typedef struct QueueNode
{
QDataType value; // 節(jié)點(diǎn)數(shù)據(jù)
struct QueueNode* next; // 指向下一個(gè)節(jié)點(diǎn)
}QNode;
1.4. 考慮效率,創(chuàng)建 頭尾指針結(jié)構(gòu)體
因?yàn)?隊(duì)列需要:隊(duì)頭 push,隊(duì)尾 pop
涉及到對(duì) 鏈表 的 尾部操作必須意識(shí)到:需要先進(jìn)行 找尾操作,時(shí)間復(fù)雜度為 O(n)
方案:因?yàn)樯婕邦^尾頻繁操作:可以 直接 同時(shí)定義 頭指針 phead 和 尾指針 ptail
技巧:同類(lèi)型的變量可以封裝成一個(gè)結(jié)構(gòu)體
因?yàn)?phead 和 ptail 是可以不斷變化的,每個(gè)相關(guān)函數(shù)都需要同時(shí)傳遞 phead 和 ptail 兩個(gè)變量
則可以將多個(gè)同類(lèi)型的變量 封裝成 一個(gè)結(jié)構(gòu)體,方便操作
這樣,傳遞變量時(shí) 直接傳遞一個(gè) 結(jié)構(gòu)體的指針就行了
typedef struct Queue
{
QNode* phead;
QNode* ptail;
}Queue;
// 區(qū)別:減少了傳遞變量的數(shù)量,利于協(xié)助開(kāi)發(fā)
// void QueuePush(QNode* phead, QNode* ptail);
void QueuePush(Queue* pq);
// void QueuePop(QNode* phead, QNode* ptail);
void QueuePop(Queue* pq);
1.5. Push / Pop :入隊(duì) 和 出隊(duì)操作
Push 在隊(duì)尾入隊(duì),Pop 在隊(duì)頭出隊(duì)
void QueuePop(Queue* pq)
{
assert(pq);
// pop 的刪除操作 需要分類(lèi)討論:鏈表節(jié)點(diǎn)個(gè)數(shù)為 0、為 1、為 兩個(gè)以上
// 為 0 :直接判空,退出操作:phead == ptail == NULL
assert(pq->phead); // 頭節(jié)點(diǎn)為空 就一定代表為空了
// 為 1:phead == ptail 但是 phead != NULL 的情況:即一定指向一個(gè)節(jié)點(diǎn)
if (pq->phead == pq->ptail && pq->phead != NULL) {
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else // 為 兩個(gè)以上:先記錄第二個(gè)節(jié)點(diǎn),free 掉頭節(jié)點(diǎn),更新頭節(jié)點(diǎn)
{
QNode* tmp = pq->phead->next;
free(pq->phead);
pq->phead = tmp;
}
}
為什么 ” 頭節(jié)點(diǎn)為空 或 尾節(jié)點(diǎn)為空 就一定代表鏈表為空了 “?
1.6. 觀察上面代碼:有需要 判斷鏈表節(jié)點(diǎn)數(shù)量的 需求,為了簡(jiǎn)化代碼與優(yōu)化過(guò)程,可以 直接定義一個(gè) size ,放進(jìn)結(jié)構(gòu)體中,時(shí)刻記錄 鏈表節(jié)點(diǎn)數(shù)量
// 結(jié)構(gòu)體更改:
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
}Queue;
// 加入 size 后 的 Push 和 Pop 函數(shù)
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->phead);
if (pq->size == 1) {
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else if (pq->size >= 2)
{
QNode* next = pq->phead->next; // 保留下一個(gè)
free(pq->phead);
pq->phead = next;
}
pq->size--; // 注意 pop 代表彈出一個(gè)節(jié)點(diǎn),數(shù)量 - 1
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
// push 前先創(chuàng)建一個(gè)新節(jié)點(diǎn)
QNode* newNode = (QNode*)malloc(sizeof(QNode));
if (newNode == NULL) {
perror("malloc fail");
return;
}
newNode->value = x;
newNode->next = NULL;
if (pq->ptail) // 若 ptail != NULL 說(shuō)明此時(shí)鏈表不為空
{
pq->ptail->next = newNode; // 舊的尾節(jié)點(diǎn)和一個(gè)新的點(diǎn) 進(jìn)行鏈接
pq->ptail = newNode; // 重新更新尾節(jié)點(diǎn)
}
else // 若鏈表為空,則 phead 和 ptail 都要 處理
{
pq->phead = pq->ptail = newNode;
}
pq->size++; // 數(shù)量++
}
2. 綜上所述,最終代碼:
Queue.c
#include"Queue.h"
// Push 入隊(duì),Pop 出隊(duì)
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->phead);
if (pq->size == 1) {
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else if (pq->size >= 2)
{
QNode* next = pq->phead->next; // 保留下一個(gè)
free(pq->phead);
pq->phead = next;
}
pq->size--; // 注意 pop 代表彈出一個(gè)節(jié)點(diǎn),數(shù)量 - 1
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
// push 前先創(chuàng)建一個(gè)新節(jié)點(diǎn)
QNode* newNode = (QNode*)malloc(sizeof(QNode));
if (newNode == NULL) {
perror("malloc fail");
return;
}
newNode->value = x;
newNode->next = NULL;
if (pq->ptail) // 若 ptail != NULL 說(shuō)明此時(shí)鏈表不為空
{
pq->ptail->next = newNode; // 舊的尾節(jié)點(diǎn)和一個(gè)新的點(diǎn) 進(jìn)行鏈接
pq->ptail = newNode; // 重新更新尾節(jié)點(diǎn)
}
else // 若鏈表為空,則 phead 和 ptail 都要 處理
{
pq->phead = pq->ptail = newNode;
}
pq->size++; // 數(shù)量++
}
// 初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
// 銷(xiāo)毀鏈表:就是 單鏈表的 銷(xiāo)毀操作
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur) {
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL; // 最后別忘了頭尾指針置為 NULL
pq->size = 0;
}
// Front 返回隊(duì)頭元素
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->phead); // 若鏈表為空 自然沒(méi)有頭節(jié)點(diǎn);
return pq->phead->value;
}
// Back 返回隊(duì)尾元素
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->ptail); // 若鏈表為空 自然沒(méi)有尾節(jié)點(diǎn);
return pq->ptail->value;
}
// Empty 判斷是否為空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
// Size 返回節(jié)點(diǎn)數(shù)量
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
Queue.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int QDataType;
typedef struct QueueNode
{
QDataType value;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
}Queue;
// 初始化
void QueueInit(Queue* pq);
// Push 入隊(duì),Pop 出隊(duì)
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
// Front 隊(duì)頭元素,Back 隊(duì)尾元素
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
// Empty 判斷是否為空,Size 返回節(jié)點(diǎn)數(shù)量
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
// 銷(xiāo)毀鏈表
void QueueDestory(Queue* pq);
Main.c
#include"Queue.h"
int main()
{
Queue q; // 創(chuàng)建隊(duì)列結(jié)構(gòu)體
QueueInit(&q); // 初始化:用于初始化鏈表的頭尾節(jié)點(diǎn):phead / ptail
for (int i = 1; i <= 5; ++i) { // 入隊(duì)列 幾個(gè)元素: 1 2 3 4 5
QueuePush(&q, i);
}
// 一個(gè)個(gè)讀取隊(duì)列元素
while (!QueueEmpty(&q))
{
printf("%d ", QueueFront(&q));
QueuePop(&q);
}
QueueDestory(&q);
return 0;
}
3. LeetCode:225.隊(duì)列實(shí)現(xiàn)棧
使用兩個(gè)隊(duì)列實(shí)現(xiàn)棧
核心思路:
保持一個(gè)隊(duì)列存數(shù)據(jù),一個(gè)隊(duì)列為空
push 入數(shù)據(jù),入到不為空的隊(duì)列
pop 出數(shù)據(jù),將 非空隊(duì)列中 前 n-1 個(gè)數(shù)據(jù) 導(dǎo)入 空隊(duì)列
代碼實(shí)現(xiàn)
// 以下均是 鏈?zhǔn)疥?duì)列的 相關(guān)函數(shù),復(fù)制粘貼過(guò)來(lái)罷了 /// typedef int QDataType; typedef struct QueueNode { QDataType value; struct QueueNode* next; }QNode; typedef struct Queue { QNode* phead; QNode* ptail; int size; }Queue; void QueuePop(Queue* pq) { assert(pq); assert(pq->phead); if (pq->size == 1) { free(pq->phead); pq->phead = pq->ptail = NULL; } else if (pq->size >= 2) { QNode* next = pq->phead->next; // 保留下一個(gè) free(pq->phead); pq->phead = next; } pq->size--; // 注意 pop 代表彈出一個(gè)節(jié)點(diǎn),數(shù)量 - 1 } void QueuePush(Queue* pq, QDataType x) { assert(pq); // push 前先創(chuàng)建一個(gè)新節(jié)點(diǎn) QNode* newNode = (QNode*)malloc(sizeof(QNode)); if (newNode == NULL) { perror("malloc fail"); return; } newNode->value = x; newNode->next = NULL; if (pq->ptail) // 若 ptail != NULL 說(shuō)明此時(shí)鏈表不為空 { pq->ptail->next = newNode; // 舊的尾節(jié)點(diǎn)和一個(gè)新的點(diǎn) 進(jìn)行鏈接 pq->ptail = newNode; // 重新更新尾節(jié)點(diǎn) } else // 若鏈表為空,則 phead 和 ptail 都要 處理 { pq->phead = pq->ptail = newNode; } pq->size++; // 數(shù)量++ } // 初始化 void QueueInit(Queue* pq) { assert(pq); pq->phead = pq->ptail = NULL; pq->size = 0; } // 銷(xiāo)毀鏈表:就是 單鏈表的 銷(xiāo)毀操作 void QueueDestory(Queue* pq) { assert(pq); QNode* cur = pq->phead; while (cur) { QNode* next = cur->next; free(cur); cur = next; } pq->phead = pq->ptail = NULL; // 最后別忘了頭尾指針置為 NULL pq->size = 0; } // Front 返回隊(duì)頭元素 QDataType QueueFront(Queue* pq) { assert(pq); assert(pq->phead); // 若鏈表為空 自然沒(méi)有頭節(jié)點(diǎn); return pq->phead->value; } // Back 返回隊(duì)尾元素 QDataType QueueBack(Queue* pq) { assert(pq); assert(pq->ptail); // 若鏈表為空 自然沒(méi)有尾節(jié)點(diǎn); return pq->ptail->value; } // Empty 判斷是否為空 bool QueueEmpty(Queue* pq) { assert(pq); return pq->size == 0; } // Size 返回節(jié)點(diǎn)數(shù)量 int QueueSize(Queue* pq) { assert(pq); return pq->size; } // 以上均是 鏈?zhǔn)疥?duì)列的 相關(guān)函數(shù),復(fù)制粘貼過(guò)來(lái)罷了 /// /// // 下面是題目主體 typedef struct { Queue q1, q2; // 創(chuàng)建兩個(gè)隊(duì)列 } MyStack; MyStack* myStackCreate() { MyStack* pst = (MyStack*)malloc(sizeof(MyStack)); // 創(chuàng)建一個(gè)棧 QueueInit(&(pst->q1)); QueueInit(&(pst->q2)); return pst; } void myStackPush(MyStack* obj, int x) { // push 到 不為空的 隊(duì)列 if(QueueEmpty(&(obj->q1))) { QueuePush(&(obj->q2), x); } else { QueuePush(&(obj->q1), x); } } int myStackPop(MyStack* obj) { // 找到非空的 隊(duì)列,將 size-1 個(gè)元素放進(jìn) 另一個(gè)空隊(duì)列,同時(shí)最后一個(gè)元素pop掉 // 有兩種情況:q1 為空,q2 不為空, q2 為空,q1 不為空 // 可以先假設(shè),后調(diào)整 // 先假設(shè) 隊(duì)列1 為空,隊(duì)列2 不為空,后面判斷后調(diào)整 Queue* pEmptyQ = &(obj->q1); Queue* pNonEmptyQ = &(obj->q2); if(!QueueEmpty(&(obj->q1))){ pEmptyQ = &(obj->q2); pNonEmptyQ = &(obj->q1); } // 將不為空隊(duì)列 的前 n-1 個(gè)元素放進(jìn) 空隊(duì)列中 while(QueueSize(pNonEmptyQ) > 1) { int x = QueueFront(pNonEmptyQ); QueuePush(pEmptyQ, x); QueuePop(pNonEmptyQ); } int t = QueueFront(pNonEmptyQ); QueuePop(pNonEmptyQ); return t; } int myStackTop(MyStack* obj) { if(QueueEmpty(&(obj->q1))) { return QueueBack(&(obj->q2)); } else { return QueueBack(&(obj->q1)); } } bool myStackEmpty(MyStack* obj) { return QueueEmpty(&(obj->q1)) && QueueEmpty(&(obj->q2)); // 兩個(gè)都為空才是???} void myStackFree(MyStack* obj) { if(QueueEmpty(&(obj->q1))) { QueueDestory(&(obj->q2)); } else if(QueueEmpty(&(obj->q2))) { QueueDestory(&(obj->q1)); } }
4. LeetCode:223.棧實(shí)現(xiàn)隊(duì)列
使用 兩個(gè)棧 模擬隊(duì)列
思路
定義一個(gè) pushStack :專(zhuān)門(mén)用來(lái)接收 push入隊(duì)列的 數(shù)據(jù)
定義一個(gè) popStack :專(zhuān)門(mén)用來(lái) pop 隊(duì)列數(shù)據(jù)
當(dāng) popStack 為空時(shí),此時(shí)需要 pop 操作,則將 pushStack 的數(shù)據(jù)全部 放進(jìn) popStack ,補(bǔ)充數(shù)據(jù)(注意是全部);若popStack 不為空,則進(jìn)行 pop 操作即可
當(dāng) 需要 push 操作,直接往 pushStack 中放數(shù)據(jù)即可
演示: push 2 次,pop 1 次,push 3 次, pop 3 次
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-859505.html
【若文章有什么錯(cuò)誤,歡迎評(píng)論區(qū)討論或私信指出】?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-859505.html
到了這里,關(guān)于【數(shù)據(jù)結(jié)構(gòu)】棧和隊(duì)列(鏈表模擬隊(duì)列)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!