C語言數據結構——隊列

目錄

0.前言

1.隊列的基本概念

2.隊列的實現

2.1實現方式

2.2具體實現

3.隊列的應用場景

4.一道隊列的算法題(LeetCode225. 用隊列實現棧)

5.結語


(圖像由AI生成)?

0.前言

在計算機科學領域,數據結構是組織和存儲數據的一種方式,它允許我們以有效的方式對數據進行訪問和修改。今天,我們將探討一種基礎但極其重要的數據結構——隊列。通過學習,我們不僅會了解隊列的理論基礎,還會深入其實現方式,探討其應用場景,并通過解決一個實際問題來鞏固我們的理解。

1.隊列的基本概念

隊列是一種基礎但非常重要的線性數據結構,它在計算機科學和編程中有著廣泛的應用。隊列遵循先進先出(FIFO, First-In-First-Out)的原則,這意味著最先被加入隊列的元素將是最先被移除的。這種特性使得隊列非常適合于那些需要按照順序處理元素的場景。

基本操作

隊列的操作通常包括:

  • 入隊(Push):在隊列的末尾添加一個新的元素。
  • 出隊(Pop):移除隊列前端的元素。
  • 查看隊首(Peek/Front):獲取隊列前端的元素,但不移除它。
  • 檢查隊列是否為空(IsEmpty):判斷隊列中是否有元素。
  • 獲取隊列大小(Size):獲取隊列中元素的數量。

特性

  • 有序性:隊列保持元素的添加順序,確保第一個加入的元素將是第一個被移除。
  • 動態性:隊列可以動態地增長和縮減,隨著元素的入隊和出隊操作。
  • 操作限制:在隊列中,只能在一端(隊尾)添加元素,在另一端(隊首)移除元素。

2.隊列的實現

2.1實現方式

隊列可以通過不同的方式實現,其中最常見的兩種是:

  1. 數組實現:使用數組存儲隊列中的元素。這種實現方式簡單直觀,但可能需要處理數組的動態擴容問題或者實現循環隊列來優化空間利用。
  2. 鏈表實現:使用鏈表存儲隊列中的元素。鏈表實現的隊列可以動態地增長,不需要擔心空間限制,但每次操作可能涉及更多的內存分配和釋放。

綜合以上因素以及隊列只需要“尾插”和“頭刪”的特性,我們最終決定用帶頭單向不循環鏈表來實現隊列。

2.2具體實現

在這一部分,我們將詳細介紹如何使用不帶頭單向不循環鏈表來實現隊列。這種實現方式利用了鏈表的動態分配特性,允許隊列在運行時根據需要增長或縮減。下面是具體的實現方法:

隊列的結構定義

首先,我們定義了兩個結構體:queueNodequeuequeueNode 結構體表示隊列中的節點,包含數據域 _data 和指向下一個節點的指針 _nextqueue 結構體則表示隊列本身,包含指向隊列頭部和尾部的指針 _head_tail

typedef struct queueNode
{QDataType _data;           // 節點存儲的數據struct queueNode* _next;   // 指向下一個節點的指針
} queueNode;typedef struct queue
{queueNode* _head;          // 指向隊列頭部的指針queueNode* _tail;          // 指向隊列尾部的指針
} queue;

初始化隊列

queueInit 函數用于初始化隊列,設置 _head_tail 指針都為 NULL,表示一個空隊列。

void queueInit(queue* pq)
{assert(pq);pq->_head = pq->_tail = NULL;
}

銷毀隊列

queueDestroy 函數用于銷毀隊列,釋放所有節點占用的內存,并將 _head_tail 指針重置為 NULL

void queueDestroy(queue* pq)
{queueNode* cur = pq->_head;while (cur){queueNode* next = cur->_next;free(cur);cur = next;}pq->_head = pq->_tail = NULL;
}

入隊操作

queuePush 函數用于在隊列尾部添加新節點。如果隊列為空,新節點即成為隊列的頭部和尾部;否則,將新節點鏈接到原尾節點的 _next 指針,并更新 _tail 指針。

void queuePush(queue* pq, QDataType x)
{assert(pq);queueNode* newNode = (queueNode*)malloc(sizeof(queueNode));if (newNode == NULL){printf("malloc fail\n");exit(-1);}newNode->_data = x;newNode->_next = NULL;if (pq->_head == NULL){pq->_head = pq->_tail = newNode;}else{pq->_tail->_next = newNode;pq->_tail = newNode;}
}

出隊操作

queuePop 函數用于移除隊列頭部的節點。如果隊列在移除節點后變為空,需要同時將 _tail 指針置為 NULL

void queuePop(queue* pq)
{assert(pq && pq->_head);queueNode* next = pq->_head->_next;free(pq->_head);pq->_head = next;if (pq->_head == NULL){pq->_tail = NULL;}
}

另外我們提供了一些基礎的隊列操作函數,如 queueFrontqueueBack 分別返回隊列的頭部和尾部元素,queueEmpty 檢查隊列是否為空,queueSize 返回隊列中元素的數量。完整代碼如下:

//queue.h
#pragma once
#ifndef QUEUE_H
#define QUEUE_H
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int QDataType;
typedef struct queueNode
{QDataType _data;struct queueNode* _next;
}queueNode;typedef struct queue
{queueNode* _head;queueNode* _tail;
}queue;void queueInit(queue* pq);
void queueDestroy(queue* pq);
void queuePush(queue* pq, QDataType x);
void queuePop(queue* pq);
QDataType queueFront(queue* pq);//返回隊頭元素
QDataType queueBack(queue* pq);//返回隊尾元素
int queueEmpty(queue* pq);//判斷隊列是否為空,為空返回1,否則返回0
int queueSize(queue* pq);//返回隊列中元素的個數#endif // !QUEUE_H
//queue.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"queue.h"
void queueInit(queue* pq)
{assert(pq);pq->_head = pq->_tail = NULL;
}void queueDestroy(queue* pq)
{queueNode* cur = pq->_head;while (cur){queueNode* next = cur->_next;free(cur);cur = next;}pq->_head = pq->_tail = NULL;
}void queuePush(queue* pq, QDataType x)
{assert(pq);queueNode* newNode = (queueNode*)malloc(sizeof(queueNode));if (newNode == NULL){printf("malloc fail\n");exit(-1);}newNode->_data = x;newNode->_next = NULL;if (pq->_head == NULL){pq->_head = pq->_tail = newNode;}else{pq->_tail->_next = newNode;pq->_tail = newNode;}
}void queuePop(queue* pq)
{assert(pq && pq->_head);queueNode*next = pq->_head->_next;free(pq->_head);pq->_head = next;if(pq->_head==NULL){pq->_tail = NULL;}
}QDataType queueFront(queue* pq)
{assert(pq && pq->_head);return pq->_head->_data;
}QDataType queueBack(queue* pq)
{assert(pq && pq->_tail);return pq->_tail->_data;
}int queueEmpty(queue* pq)
{assert(pq);return pq->_head == NULL ? 1 : 0;
}int queueSize(queue* pq)
{assert(pq);queueNode* cur = pq->_head;int count = 0;while (cur){count++;cur = cur->_next;}return count;
}

3.隊列的應用場景

隊列作為一種基本的數據結構,在計算機科學及其相關領域中有著廣泛的應用。其先進先出(FIFO)的特性使得隊列非常適合于處理需要按順序執行的任務。以下是隊列在不同領域中的一些典型應用場景:(以下查詢自網絡)

  • 操作系統
  • 在操作系統中,隊列被用于多種任務調度和管理過程。例如,CPU調度算法如先來先服務(FCFS)直接使用隊列的FIFO特性來管理進程。進程控制塊(PCB)根據進程到達的順序排隊等待CPU時間。此外,I/O請求管理也常使用隊列,設備請求按照到達順序排隊等待處理。
  • 網絡通信
  • 在網絡通信中,隊列用于管理數據包的傳輸。數據包在進入網絡設備如路由器或交換機時排隊等待處理。這種機制幫助控制網絡擁塞,確保數據包以合理的順序被轉發。
  • 異步編程
  • 在異步編程模型中,隊列用于管理事件或消息。應用程序將事件放入隊列中,事件循環依次處理這些事件。這種模型在JavaScript等語言的事件驅動編程中尤為常見。
  • 打印任務管理
  • 在打印任務管理中,打印任務按照提交的順序排隊等待打印。這確保了文檔將按照用戶提交的順序被打印,避免了因資源競爭造成的混亂。

4.一道隊列的算法題(LeetCode225. 用隊列實現棧)

學習了棧和隊列的知識之后,我們不妨來看一道簡單的算法題——用隊列實現棧。(鏈接在上面)

原題大致如下:

請你僅使用兩個隊列實現一個后入先出(LIFO)的棧,并支持普通棧的全部四種操作(pushtoppop?和?empty)。

實現?MyStack?類:

  • void push(int x)?將元素 x 壓入棧頂。
  • int pop()?移除并返回棧頂元素。
  • int top()?返回棧頂元素。
  • boolean empty()?如果棧是空的,返回?true?;否則,返回?false?。

注意:

  • 你只能使用隊列的基本操作 —— 也就是?push to backpeek/pop from frontsize?和?is empty?這些操作。
  • 你所使用的語言也許不支持隊列。?你可以使用 list (列表)或者 deque(雙端隊列)來模擬一個隊列?, 只要是標準的隊列操作即可。

以下是用C語言的實現代碼。由于C語言庫函數沒有“隊列”,我們只能先“造”出一個隊列。

typedef int QueueDataType;// 定義隊列節點結構體
typedef struct QueueNode
{QueueDataType data;         // 節點存儲的數據struct QueueNode* next;     // 指向下一個節點的指針
} Node;// 定義隊列結構體
typedef struct Queue
{Node* head;                 // 指向隊列頭節點的指針Node* tail;                 // 指向隊列尾節點的指針size_t size;                // 隊列中元素的數量
} Queue;// 初始化隊列
void QueueInit(Queue* q)
{// 創建哨兵節點q->head = q->tail = (Node*)malloc(sizeof(Node));q->head->next = NULL;q->size = 0;
}// 向隊列中添加元素
void QueuePush(Queue* q, QueueDataType x)
{Node* newNode = (Node*)malloc(sizeof(Node));newNode->data = x;newNode->next = NULL;q->tail->next = newNode;q->tail = newNode;q->size++;
}// 從隊列中移除元素
void QueuePop(Queue* q)
{assert(q->head->next != NULL); // 確保隊列非空Node* toDelete = q->head->next;q->head->next = toDelete->next;if (q->tail == toDelete) // 如果移除的是尾節點,更新tail指向哨兵節點{q->tail = q->head;}free(toDelete);q->size--;
}// 獲取隊列頭部元素
QueueDataType QueueFront(Queue* q)
{assert(q->head->next != NULL);return q->head->next->data;
}// 獲取隊列尾部元素
QueueDataType QueueBack(Queue* q)
{assert(q->head->next != NULL);return q->tail->data;
}// 檢查隊列是否為空
bool QueueEmpty(Queue* q)
{return q->head->next == NULL;
}// 獲取隊列中元素的數量
size_t QueueSize(Queue* q)
{return q->size;
}// 銷毀隊列,釋放內存
void QueueDestroy(Queue* q)
{while (q->head->next != NULL){QueuePop(q);}free(q->head);q->head = q->tail = NULL;
}// 定義棧結構體,內部使用兩個隊列實現
typedef struct {Queue q1;   // 主隊列Queue q2;   // 輔助隊列,用于實現棧的pop和top操作
} MyStack;// 創建并初始化棧
MyStack* myStackCreate() {MyStack* stack = (MyStack*)malloc(sizeof(MyStack));QueueInit(&(stack->q1));QueueInit(&(stack->q2));return stack;
}// 向棧中添加元素
void myStackPush(MyStack* obj, int x) {if(QueueEmpty(&(obj->q2))) // 如果q2為空,向q1中添加元素{QueuePush(&(obj->q1), x);}else // 如果q1為空,向q2中添加元素{QueuePush(&(obj->q2), x);}
}// 從棧中移除元素
int myStackPop(MyStack* obj) {if(QueueEmpty(&(obj->q2))) // 如果q2為空,從q1中移動元素到q2,直到最后一個元素{while(obj->q1.size > 1){int num = QueueFront(&(obj->q1));QueuePop(&(obj->q1));QueuePush(&(obj->q2), num);}int ret = QueueFront(&(obj->q1));QueuePop(&(obj->q1));return ret;}else // 如果q1為空,從q2中移動元素到q1,直到最后一個元素{while(obj->q2.size > 1){int num = QueueFront(&(obj->q2));QueuePop(&(obj->q2));QueuePush(&(obj->q1), num);}int ret = QueueFront(&(obj->q2));QueuePop(&(obj->q2));return ret;}
}// 獲取棧頂元素
int myStackTop(MyStack* obj) {if(QueueEmpty(&(obj->q2))) // 如果q2為空,棧頂元素在q1的尾部{return QueueBack(&(obj->q1));}else // 如果q1為空,棧頂元素在q2的尾部{return QueueBack(&(obj->q2));}
}// 檢查棧是否為空
bool myStackEmpty(MyStack* obj) {return QueueEmpty(&(obj->q1)) && QueueEmpty(&(obj->q2));
}// 銷毀棧,釋放內存
void myStackFree(MyStack* obj) {QueueDestroy(&(obj->q1));QueueDestroy(&(obj->q2));free(obj);
}

實現原理

該實現策略涉及兩個隊列,q1q2,它們交替作為主隊列和輔助隊列。這種方法的核心在于利用隊列的先進先出(FIFO)特性來模擬棧的后進先出(LIFO)特性。

  • Push 操作:總是向當前非空的隊列中添加新元素。如果兩個隊列都為空,則可以選擇任意一個隊列進行添加。這保證了所有元素都存儲在同一個隊列中,而另一個隊列處于空閑狀態,待下一次poptop操作時使用。

  • Pop 操作:將主隊列中的所有元素(除最后一個元素)依次出隊并入隊到輔助隊列中,然后移除并返回主隊列中的最后一個元素。通過這種方式,可以實現棧的LIFO特性。操作完成后,主隊列和輔助隊列的角色會互換。

  • Top 操作:與pop操作類似,但是不移除最后一個元素,只返回其值。

  • Empty 操作:當兩個隊列都為空時,棧也為空。

代碼解析

在以上代碼中,Queue結構體用于實現隊列的基本功能,而MyStack結構體則使用兩個Queue實例來模擬棧的行為。

  • myStackPush函數向當前活躍的隊列中添加元素。
  • myStackPop函數通過將主隊列中的元素(除了最后一個)轉移到輔助隊列中,然后移除并返回最后一個元素,實現了棧的pop操作。此后,隊列的角色將互換。
  • myStackTop函數與myStackPop類似,但是在返回最后一個元素的值后,不會將其移除。
  • myStackEmpty函數檢查兩個隊列是否都為空,以確定棧是否為空。
  • myStackFree函數負責釋放兩個隊列和棧實例所占用的內存資源。

5.結語

隊列是數據結構中的基石之一,了解其原理和實現方式對于學習更復雜的數據結構和算法至關重要。通過實際編碼和解決問題,我們可以加深對隊列結構和其應用的理解。希望這篇博客能幫助你在數據結構的學習旅程中邁出堅實的一步。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/714632.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/714632.shtml
英文地址,請注明出處:http://en.pswp.cn/news/714632.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux篇: 進程控制

一、進程創建 1.1 fork函數初識 在Linux中&#xff0c;fork函數是非常重要的函數&#xff0c;它從已存在進程中創建一個新進程。新進程為子進程&#xff0c;而原進程為父進程。 返回值&#xff1a; 在子進程中返回0&#xff0c;父進程中返回子進程的PID&#xff0c;子進程創…

OSI七層模型/TCP四層模型

協議&#xff1a; 協議是雙方共同指定的一組規則&#xff0c;在網絡通信中表示通信雙方傳遞數據和解釋數據的一組規則。 從A上傳文件到服務器B,需要在A和B之間制定一個雙方都認可的規則&#xff0c;這個規則就叫文件傳輸協議&#xff0c;該協議是ftp協議的一個初級版本&#…

LeetCode 刷題 [C++] 第226題.翻轉二叉樹

題目描述 給你一棵二叉樹的根節點 root &#xff0c;翻轉這棵二叉樹&#xff0c;并返回其根節點。 題目分析 深度優先搜索&#xff08;DFS&#xff09;- 遞歸方式 對于二叉樹的鏡像問題&#xff0c;很容易想到的就是使用遞歸來解決&#xff0c;自底向上依次翻轉每一個節點…

2024年騰訊云優惠券領取頁面_代金券使用方法_新老用戶均可

騰訊云代金券領取渠道有哪些&#xff1f;騰訊云官網可以領取、官方媒體賬號可以領取代金券、完成任務可以領取代金券&#xff0c;大家也可以在騰訊云百科蹲守代金券&#xff0c;因為騰訊云代金券領取渠道比較分散&#xff0c;騰訊云百科txybk.com專注匯總優惠代金券領取頁面&am…

『大模型筆記』Sora:探索大型視覺模型的前世今生、技術內核及未來趨勢

Sora:探索大型視覺模型的前世今生、技術內核及未來趨勢 文章目錄 一. 摘要二. 引言楊立昆推薦的關于世界模型的真正含義(或應該是什么)的好文章。原文:Sora: A Review on Background, Technology, Limitations, and Opportunities of Large Vision Models譯文:Sora探索大型…

百度SEO快排原理是什么?如何快速排名方法?

前言&#xff1a;我之前說過我不打算寫這個快速排序。 首先&#xff0c;我從來沒有在自己的網站上操作過所謂的快速排序。 其次&#xff0c;我不能像網上很多人寫的那樣透露百度快速排序的秘密&#xff08;說實話&#xff0c;你可以透露秘密&#xff09;。 方法是有了&#xff…

Linux系統運維腳本:編寫bash腳本程序監控服務器的磁盤空間,在磁盤使用率超過閾值時發送警告郵件

目 錄 一、要求 二、解決方案 &#xff08;一&#xff09;解決思路 &#xff08;二&#xff09;方案 三、腳本程序實現 &#xff08;一&#xff09;腳本代碼和解釋 1、腳本代碼 2、代碼解釋 &#xff08;二&#xff09;腳本驗證 1、腳本編輯 2、給予執…

使用遞歸求解數組最大值(c++題解)

題目描述 輸入一個整數n&#xff08;n不大于1000&#xff09;&#xff0c;接下來分別為n個整數&#xff0c;請使用遞歸求取最大值。 輸入格式 第一行&#xff1a;正整數n。 第二行&#xff1a;n個整數。 輸出格式 輸出最大值 樣例 樣例輸入 復制2 1 2樣例輸出 復制2 …

Postman: 前端必備工具還是后端獨享利器

Postman 的使用場景&#xff1a;適用于前端和后端 Postman 是一個流行的 API 測試與開發工具。它被廣泛地應用在前后端開發的過程中&#xff0c;但是很多人對于它的使用場景存在疑惑。那么&#xff0c;到底是前端用還是后端用呢&#xff1f;本文將從多個角度詳細解答這個問題。…

Node.js_基礎知識(CommonJS模塊化)

CommonJS模塊化規范 加載時機&#xff1a; 服務器端: 模塊的加載是運行時同步加載的&#xff0c;node.js實現了模塊化規范瀏覽器端: 模塊需要提前編譯打包處理&#xff0c;需使用Browserify編譯打包&#xff0c;推薦使用ESM 暴露模塊&#xff1a;module.exports、exports導入模…

“а”搭配使用更地道,柯橋外貿俄語培訓

1、а именно 就是說&#xff0c;就是&#xff0c;正是 例&#xff1a; в то время, а именно год назад. 那時, 也就是一年前。 не кто иной, а именно г-н Ван. 不是別人&#xff0c;就是王先生 2、а наоборот …

【嵌入式——QT】QListWidget

QListWidget類提供了一個基于項的列表小部件&#xff0c;QListWidgetItem是列表中的項&#xff0c;該篇文章中涉及到的功能有添加列表項&#xff0c;插入列表項&#xff0c;刪除列表項&#xff0c;清空列表&#xff0c;向上移動列表項&#xff0c;向下移動列表項。 常用API a…

C語言數據結構基礎——雙鏈表專題

前言 書接上回&#xff0c;雙鏈表便是集齊帶頭、雙向、循環等幾乎所有元素的單鏈表PLUS. 1.初始化、創建雙鏈表 typedef int LTDataType; typedef struct LTNode {LTDataType data;struct LTNode* next;struct LTNode* prev; }LTNode; 不同于單鏈表&#xff0c;此時每個節點應…

selenium初始學習--打開新標簽操作

selenium 打開新標簽操作 簡單說一下使用 環境 &#xff1a;python 3.9 selenium 4,18 初始化操作 目的 打開bilibilie網站并搜索視頻&#xff08;電影&#xff09; 并點擊觀看 操作 打開應用并搜索網址 from selenium import webdriver import timefrom selenium.webdr…

PySide6+VSCode Python可視化環境搭建

#記住在cmd中運行&#xff0c;不要在vscode里運行&#xff0c;否則env會裝到工程目錄下 python -m venv env #env\Scripts\activate.bat pip install pyside6 下載本期源碼 vscode裝一個PYQT Integration插件&#xff0c;設置好兩個路徑&#xff08;下面有個腳本用于獲取路徑&…

MySQL 數據庫表設計和優化

一、數據結構設計 正確的數據結構設計對數據庫的性能是非常重要的。 在設計數據表時&#xff0c;盡量遵循一下幾點&#xff1a; 將數據分解為合適的表&#xff0c;每個表都應該有清晰定義的目的&#xff0c;避免將過多的數據存儲在單個表中。使用適當的數據類型來存儲數據&…

2020小學甲組--恢復數組

題目描述 有一個數組a[1..n]&#xff0c;但是這個數組的內容丟失了&#xff0c;你要嘗試恢復它。已知以下的三個事實&#xff1a; 1、對于1<i<n&#xff0c;都有a[i]>0&#xff0c;且所有的a[i]互不相同。即a數組保存的全部都是正整數&#xff0c;且互不相同。 2、…

挑戰杯 基于機器視覺的車道線檢測

文章目錄 1 前言2 先上成果3 車道線4 問題抽象(建立模型)5 幀掩碼(Frame Mask)6 車道檢測的圖像預處理7 圖像閾值化8 霍夫線變換9 實現車道檢測9.1 幀掩碼創建9.2 圖像預處理9.2.1 圖像閾值化9.2.2 霍夫線變換 最后 1 前言 &#x1f525; 優質競賽項目系列&#xff0c;今天要分…

范偉:你們怎么老提1,200呢,有什么典故啊?趙本山:沒有啊!

范偉&#xff1a;你們怎么老提1,200呢,有什么典故啊?趙本山&#xff1a;沒有啊&#xff01; --小品《面子》&#xff08;中3&#xff09;的臺詞 表演者&#xff1a;趙本山 高秀敏 范偉 &#xff08;接上&#xff09; 范偉&#xff1a;哎吃啊 趙&#xff1a;哎呀這電視看的挺…

Acwing枚舉、模擬與排序(一)

連號區間數 原題鏈接&#xff1a;https://www.acwing.com/problem/content/1212/ 初始最小值和最大值的依據是題目給出的數據范圍。只要在數據范圍之外就可以。 連號的時候&#xff0c;相鄰元素元素之間&#xff0c;差值為1。那么區間右邊界和左邊界&#xff0c;的值的差&#…