【數據結構】棧和隊列——棧

目錄

  • 棧和隊列
      • 棧的基本概念
      • 棧的順序存儲實現
        • 棧的定義與初始化
        • 入棧操作
        • 出棧操作
        • 讀取棧頂元素
        • 判空和判滿操作
        • 棧的銷毀操作
        • 操作集合

棧和隊列

棧的基本概念

棧的定義:

  • 棧(Stack) 是一種線性表,它限定了數據元素的插入和刪除操作只能在表的一端進行
  • 允許操作的一端稱為 棧頂(Top),另一端稱為 棧底(Bottom)

所以,棧就是 “先進后出(FILO, First In Last Out)” 的數據結構。

棧的特點:

  • 受限性:只能在棧頂進行插入(push)和刪除(pop)。
  • 線性結構:元素有先后次序。
  • FILO 特性:最先入棧的元素最后出棧。

形象理解:就像一摞盤子,先放進去的盤子在最下面,拿的時候只能從最上面一個個拿走。

棧的基本操作:

  • Push(入棧):在棧頂插入一個新元素。
  • Pop(出棧):刪除棧頂元素,并返回該元素。
  • GetTop(讀棧頂元素):僅僅讀取棧頂元素,不刪除。
  • InitStack(初始化棧):建立一個空棧。
  • StackEmpty(判空):檢查棧是否為空。
  • StackFull(判滿,順序棧時需要):判斷棧是否已滿。

棧的存儲結構:

  1. 順序棧:
    • 用數組存儲棧元素。
    • 優點:實現簡單,存取快。
    • 缺點:容量固定,可能溢出(上溢/下溢)。
      在這里插入圖片描述
  2. 鏈式棧
    • 用鏈表存儲,棧頂一般在鏈表的表頭。
    • 優點:容量不受限制。
    • 缺點:需要額外指針,操作相對慢。
      在這里插入圖片描述

棧的順序存儲實現

棧的定義與初始化

順序棧是棧的一種順序存儲結構,用數組(連續內存)來存放數據元素。特點:

  1. 棧頂指針(top)指向棧頂元素的位置(或空元素的下一個位置)。
  2. 棧容量固定(靜態)或可擴展(動態)。
  3. 支持的基本操作:
    • 初始化棧 InitStack
    • 入棧 Push
    • 出棧 Pop
    • 判空 IsEmpty
    • 獲取棧頂元素 GetTop

順序棧top 指針的指向在不同教材中有差異:

  1. top 指向棧頂元素(經典寫法,大部分算法書和面試中用的)
    • top 是數組下標
    • 空棧時:top = -1
    • 入棧:先 top++,再存元素
    • 出棧:取 data[top],再 top--
    • 特點:top 總是指向有效元素的位置
  2. top 指向棧頂下一個空位(部分教材/視頻中用)
    • top 是指針
    • 空棧時:top = 0
    • 入棧:直接存到 data[top],然后 top++
    • 出棧:先 top--,再取 data[top]
    • 特點:top 表示棧中元素數量,也就是下一個可用位置

兩種方法邏輯不同,但功能一樣,只是 top 的含義不同

靜態順序棧:

  • 定義:
    #define MAXSIZE 100
    typedef int SElemType;  // 假設存儲 int 類型typedef struct {SElemType *base;   // 棧底指針,指向數組首地址SElemType *top;    // 棧頂指針,指向棧頂元素的下一個位置int stacksize;     // 棧容量
    } SqStack;
    
  • 初始化:
    // 初始化
    void InitStack(SqStack *S) {S->base = data;      // base 指向數組首地址S->top = S->base;    // 空棧,top 指向第一個空位S->stacksize = MAXSIZE;
    }
    
    特點
    • 容量固定,無法擴容
    • top 指向棧頂元素的下一個位置
    • 入棧/出棧時用 *(S->top++) = e / *e = *(--S->top)

動態順序棧: 動態順序棧可以在棧滿時自動擴容。

  • 定義:
    typedef int SElemType;typedef struct {SElemType *base;   // 棧底指針SElemType *top;    // 棧頂指針,指向棧頂元素的下一個位置int stacksize;     // 當前容量
    } SqStack;
    
  • 初始化:
    // 初始化
    int InitStack(SqStack *S, int initSize) {S->base = (SElemType *)malloc(initSize * sizeof(SElemType));if (!S->base) return 0;   // 分配失敗S->top = S->base;         // 空棧S->stacksize = initSize;return 1;
    }
    

動態順序棧可以在 Push 時判斷容量是否夠,如果不夠就 realloc 擴容。

#include <stdio.h>
#include <stdlib.h>typedef int SElemType;  // 統一元素類型typedef struct {SElemType *base;    // 棧底指針SElemType *top;     // 棧頂指針(指向棧頂元素的下一個位置)int stacksize;      // 當前容量
} SqStack;// 初始化棧
int InitStack(SqStack *S, int initSize) {S->base = (SElemType *)malloc(initSize * sizeof(SElemType));if (!S->base) return 0;  // 分配失敗S->top = S->base;        // 空棧:棧頂=棧底S->stacksize = initSize;return 1;
}// 入棧
int Push(SqStack *S, SElemType x) {// 判斷棧滿,需要擴容if (S->top - S->base >= S->stacksize) {int newSize = S->stacksize * 2;SElemType *newBase = (SElemType *)realloc(S->base, newSize * sizeof(SElemType));if (!newBase) return 0;  // 擴容失敗// 更新指針和容量S->top = newBase + (S->top - S->base);  // 調整棧頂指針(原偏移量不變)S->base = newBase;S->stacksize = newSize;printf("棧擴容到 %d\n", newSize);}*(S->top++) = x;  // 先賦值,再移動棧頂指針return 1;
}// 出棧(將棧頂元素存入e,成功返回1,失敗返回0)
int Pop(SqStack *S, SElemType *e) {if (S->top == S->base) return 0;  // 空棧*e = *(--S->top);  // 先移動棧頂指針,再取值return 1;
}int main() {SqStack S;if (!InitStack(&S, 3)) {  // 初始容量3printf("棧初始化失敗\n");return 1;}// 入棧10個元素(測試擴容)for (int i = 1; i <= 10; i++) {if (Push(&S, i)) {printf("入棧: %d, 當前元素數: %d\n", i, S.top - S.base);} else {printf("入棧 %d 失敗\n", i);}}// 出棧所有元素SElemType e;printf("\n出棧順序:\n");while (Pop(&S, &e)) {printf("出棧: %d\n", e);}free(S.base);  // 釋放堆內存return 0;
}
入棧操作

入棧(Push) 就是把一個新元素壓入棧頂的操作。
核心邏輯:

  1. 判斷棧是否已滿(靜態棧)或是否需要擴容(動態棧)。
  2. 棧頂指針 top 上移(或計算位置)。
  3. 將元素放到棧頂位置。
  4. 更新 top(或數組索引)。

棧遵循 后進先出(LIFO) 原則,所以新元素總是放在棧頂。

靜態順序棧入棧:

void Push(SqStack *S, int e) {// 判斷是否棧滿if (S->top - S->base >= S->stacksize) {return 0;   // 入棧失敗}*(S->top) = e;  // 把元素放到 top 指向的位置S->top++;       // top 后移,指向下一個空位return 1;       // 入棧成功
}
  • 空棧時top == base
  • 入棧成功后top 始終指向“棧頂元素的下一個位置”。
  • 棧滿條件top - base == stacksize
  • 棧頂元素*(top - 1)

動態順序棧入棧:

// 入棧
void Push(SqStack *S, int e) {if (S->top - S->base >= S->stacksize) {   // 棧滿,需要擴容S->base = (ElemType *)realloc(S->base, (S->stacksize + STACKINCREMENT) * sizeof(ElemType));if (!S->base) exit(0); // 內存不足S->top = S->base + S->stacksize;  // 重新定位 topS->stacksize += STACKINCREMENT;   // 更新容量}*(S->top) = e;   // 把元素放到棧頂S->top++;        // top 指針上移
}

動態順序棧入棧圖示:

   初始棧(容量=3)         入棧擴容后(容量=6)
+----+----+----+        +----+----+----+----+----+----+
| 10 | 20 | 30 |        | 10 | 20 | 30 | 40 |    |    |
+----+----+----+        +----+----+----+----+----+----+↑                                ↑top                              top
出棧操作
// 出棧操作:將棧頂元素彈出并返回給 e
int Pop(SqStack *S, int *e) {if (S->top == S->base) return 0;   // 棧空S->top--;                              // 指針下移*e = *(S->top);                        // 取出元素return 1;
}

S->top--;*e = *(S->top); 可以合并為 *e = *(--S->top);

有道例題:寫出下列程序段的輸出結果

void main(){Stack S;InitStack(S);x='c'; y='k';Push(S,x); Push(S,'a'); Push(S,y);Pop(S,x);  Push(S,'t'); Push(S,x);Pop(S,x);  Push(S,'s');While(!StackEmpty(S)) {Pop(S,y); printf(y);}printf(x);
}

Pop 會將出棧的數據返回,在這題中的重點是 Pop 操作會改變原來 x 中的數據

讀取棧頂元素

核心思想

  • 棧頂元素位于 top 的前一個位置(因為 top 指向棧頂元素的下一個空位)。
  • 不改變 top 指針的值,只是返回棧頂元素。
// 取棧頂元素,不出棧
int GetTop(SqStack S, int *e) {if (S.top == S.base) return ERROR; // 空棧*e = *(S.top - 1);                // 棧頂元素return 1;
}
  • top 指向棧頂元素的下一個位置
  • 棧頂元素*(top - 1)
  • GetTop 不改變棧結構,只返回棧頂元素;
判空和判滿操作
  1. 判空操作:
    核心思想:棧空時,top == base,因為沒有元素,棧頂指針就在棧底。
    // 判空
    int StackEmpty(SqStack S) {if (S.top == S.base) return 1;   // 棧空else return 0;   // 棧非空
    }
    
  2. 判滿操作
    核心思想:棧滿時,top - base == stacksize,因為棧頂指針指向下一個空位,減去棧底得到元素個數等于容量。
    // 判滿
    int StackFull(SqStack S) {if (S.top - S.base == S.stacksize)return 1;   // 棧滿elsereturn 0;   // 棧未滿
    }
    
棧的銷毀操作

核心思想

  • 棧底指針 base 是動態分配的,所以只需要 free(base)
  • 銷毀后,將 basetop 置為 NULLstacksize 置為 0,防止野指針。
Status DestroyStack(SqStack *S) {if (S->base) {free(S->base);   // 釋放動態分配的內存S->base = NULL;S->top = NULL;S->stacksize = 0;return OK;}return ERROR;         // 棧本身為空或未初始化
}
  • 棧使用 malloc 分配內存后,必須用 free 釋放,否則會 內存泄漏
  • 銷毀后把 basetop 置為 NULLstacksize 置 0,避免野指針。
  • 對靜態順序棧(數組固定分配)則不需要銷毀操作,因為數組在棧上分配,程序結束自動釋放。
操作集合
#include <stdio.h>
#include <stdlib.h>#define MAXSIZE 100
#define OK 1
#define ERROR 0typedef int Status;
typedef int SElemType;// 順序棧定義(老師寫法)
typedef struct {SElemType *base;   // 棧底指針SElemType *top;    // 棧頂指針(指向下一個空位)int stacksize;     // 棧容量
} SqStack;// 初始化棧
Status InitStack(SqStack *S) {S->base = (SElemType *)malloc(MAXSIZE * sizeof(SElemType));if (!S->base) return ERROR;S->top = S->base;S->stacksize = MAXSIZE;return OK;
}// 入棧
Status Push(SqStack *S, SElemType e) {if (S->top - S->base == S->stacksize) return ERROR; // 棧滿*(S->top) = e;S->top++;return OK;
}// 出棧
Status Pop(SqStack *S, SElemType *e) {if (S->top == S->base) return ERROR; // 空棧S->top--;*e = *(S->top);return OK;
}// 取棧頂元素(不出棧)
Status GetTop(SqStack S, SElemType *e) {if (S.top == S.base) return ERROR; // 空棧*e = *(S.top - 1);return OK;
}// 判空
Status StackEmpty(SqStack S) {return S.top == S.base ? 1 : 0;
}// 判滿
Status StackFull(SqStack S) {return (S.top - S.base == S.stacksize) ? 1 : 0;
}// 銷毀棧
Status DestroyStack(SqStack *S) {if (S->base) {free(S->base);S->base = NULL;S->top = NULL;S->stacksize = 0;return OK;}return ERROR;
}// 打印棧內元素(從棧底到棧頂)
void PrintStack(SqStack S) {SElemType *p = S.base;while (p < S.top) {printf("%d ", *p);p++;}printf("\n");
}// 測試順序棧操作
int main() {SqStack S;if (InitStack(&S) == ERROR) {printf("棧初始化失敗!\n");return 0;}printf("棧是否為空: %d\n", StackEmpty(S));// 入棧for (int i = 1; i <= 5; i++) {Push(&S, i);printf("入棧: %d, 棧頂元素: %d\n", i, *(S.top - 1));}printf("當前棧: ");PrintStack(S);printf("棧是否已滿: %d\n", StackFull(S));// 取棧頂元素SElemType e;if (GetTop(S, &e) == OK) {printf("棧頂元素: %d\n", e);}// 出棧while (!StackEmpty(S)) {Pop(&S, &e);printf("出棧元素: %d\n", e);}printf("棧是否為空: %d\n", StackEmpty(S));// 銷毀棧DestroyStack(&S);printf("銷毀棧后, 棧容量: %d, base指針: %p\n", S.stacksize, S.base);return 0;
}

程序運行結果如下:
在這里插入圖片描述

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

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

相關文章

大數據管理與應用系列叢書《數據挖掘》讀書筆記之集成學習(1)

文章目錄前言一、集成學習是什么&#xff1f;1.基本思想2.集成學習的類型3. 集成學習的結合策略3.1 為什么結合策略是集成學習的靈魂&#xff1f;3.2 經典策略(1)**投票法&#xff08;Voting&#xff09;****(2)平均法&#xff08;Averaging&#xff09;****(3) 學習法**3.3 關…

嵌入式知識篇---32GUI

要理解 32 位單片機的 GUI&#xff0c;咱們先從 “基礎概念” 入手&#xff0c;再拆成 “為什么能跑 GUI”“核心組成”“怎么實現”“常用工具”“實際用途” 這幾步講&#xff0c;全程不用復雜術語&#xff0c;像聊日常用品一樣說清楚。一、先搞懂 2 個基礎概念在講 “32 位單…

【iOS】SDWebImage第三方庫源碼學習筆記

前言之前在寫項目時&#xff0c;經常用到SDWebImage這個第三方庫來加載圖片&#xff0c;并且了解到了這個第三方庫在處理圖片時自帶異步下載和緩存功能&#xff0c;以及對cell復用的處理。這篇文章來系統學習一下SDWebImage第三方庫的知識以及底層原理簡介SDWebImage為UIImageV…

Linux --網絡基礎概念

一.網絡發展獨立模式&#xff1a;在早期計算機之間是相互獨立的&#xff0c;機器之間的數據只能通過軟硬盤來傳輸&#xff0c;這就代表無法同時完成任務&#xff0c;需要前面的計算機完成各自的任務經過硬盤傳遞數據再完成自己的任務&#xff0c;效率十分低下。網絡互聯&#x…

教育系統搭建攻略:線上知識付費與線下消課排課全解析

作為一名資深平臺測評師&#xff0c;最近我挖到了一個教育機構的 “寶藏工具”—— 喬拓云教育系統。別看它名字低調&#xff0c;用起來那叫一個順手&#xff0c;線上知識付費、線下消課排課全給你安排得明明白白&#xff0c;簡直是機構老板和教務員的 “摸魚神器”。多端口管理…

PMP項目管理知識點-①項目基本概念

目錄 1.項?的定義 概念&#xff1a; 特點&#xff1a; 項?與運營的區別 項?特點&#xff1a; 運營特點&#xff1a; 2.項?管理的發展 3.項?、項?集與項?組合 結構層次 4.項?的關鍵組成部分 項??命周期&#xff1a; 項?管理過程組&#xff1a; 項?階段&…

Python內置函數全解析:30個核心函數語法、案例與最佳實踐指南

&#x1f49d;&#x1f49d;&#x1f49d;歡迎蒞臨我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 持續學習&#xff0c;不斷…

數據建模怎么做?一文講清數據建模全流程

目錄 一、需求分析 1. 搞清楚業務目標&#xff1a;這數據是要解決啥問題&#xff1f; 2. 明確數據邊界&#xff1a;哪些數據該要&#xff0c;哪些不該要&#xff1f; 3. 弄明白使用場景&#xff1a;誰用這數據&#xff0c;怎么用&#xff1f; 二、模型設計 1. 第一步&…

胸部X光片數據集:健康及肺炎2類,14k+圖像

胸部X光片數據集概述 數據集包含14090張圖像,分為正常胸部X光3901張,肺炎胸部X光10189張。 標注格式:無標注,文件夾分類。 圖像尺寸:640*640 正常胸部X光: 肺炎胸部X光: 數據采集: 拍攝方式:均為前后位(anterior-posterior)胸部X光,屬患者常規臨床護理的一部分…

MySQL數據庫開發教學(二) 核心概念、重要指令

書接上回&#xff1a;MySQL數據庫開發教學(一) 基本架構-CSDN博客 建議工具&#xff1a; Navicat Premium (收費 / 需破解)&#xff1a;Navicat Premium | 管理和開發你的數據庫 phpstudy 2018 (免費)&#xff1a;phpStudy - Windows 一鍵部署 PHP 開發環境 小皮出品 前言 …

【40頁PPT】數字工廠一體化運營管控平臺解決方案(附下載方式)

篇幅所限&#xff0c;本文只提供部分資料內容&#xff0c;完整資料請看下面鏈接 https://download.csdn.net/download/2501_92808811/91716541 資料解讀&#xff1a;【40頁PPT】數字工廠一體化運營管控平臺解決方案 詳細資料請看本解讀文章的最后內容。該資料圍繞數字工廠一體…

數據產品(2)用戶畫像數據分析模型

目錄 1 用戶畫像 2 RFM模型 (用戶價值分群模型) 3 PSM 價格敏感度 4 精細化運營 1 用戶畫像 也稱用戶表標簽,是基于用戶行為分析獲得的對用戶的一種認知表達,即用戶數據標簽化,通過收集與分析用戶的用戶屬性(年齡、性別、城市、職業、設備、狀態)、用戶偏好(購物偏好,聽…

03_數據結構

第3課&#xff1a;數據結構 課程目標 掌握Python的基本數據結構&#xff1a;列表、元組、字典、集合學習字符串的高級操作方法理解不同數據結構的特點和適用場景 1. 列表&#xff08;List&#xff09; 1.1 列表的創建和基本操作 # 創建列表 fruits ["蘋果", "香…

【JavaEE】多線程 -- CAS機制(比較并交換)

目錄CAS是什么CAS的應用實現原子類實現自旋鎖ABA問題ABA問題概述ABA問題引起的BUG解決方案CAS是什么 CAS (compare and swap) 比較并交換&#xff0c;CAS 是物理層次支持程序的原子操作。說起原子性&#xff0c;這就設計到線程安全問題&#xff0c;在代碼的層面為了解決多線程…

The United Nations Is Already Dead

The United Nations Is Already Dead When children in Gaza rummage through rubble for food, when UN-run schools are reduced to dust, when the Security Council cannot even pass the mildest ceasefire resolution—blocked by a single veto— we must confront a br…

Kubernetes v1.34 前瞻:資源管理、安全與可觀測性的全面進化

預計正式發布&#xff1a;2025年8月底 | 分類&#xff1a;Kubernetes 隨著2025年8月底的臨近&#xff0c;Kubernetes社區正緊鑼密鼓地準備下一個重要版本——v1.34的發布。本次更新并非簡單的功能疊加&#xff0c;而是在資源管理、安全身份、可觀測性和工作負載控制等核心領域的…

用 Bright Data MCP Server 構建實時數據驅動的 AI 情報系統:從市場調研到技術追蹤的自動化實戰

前言 本文通過兩個真實場景&#xff08;云服務商對比與 AIGC 技術追蹤&#xff09;&#xff0c;展示了如何使用 Bright Data MCP Server 與 Lingma IDE 構建一個具備實時網頁數據抓取、結構化分析與自動化報告生成能力的 AI 工作流。通過簡單的 API 調用與 JSON 配置&#xff…

牛頓第二定律的所有表達方式:1、線性表達 2、圓形表達 3、雙曲線表達 4、拋物線表達5、數列表達

牛頓第二定律是經典力學中的核心定律&#xff0c;表述為&#xff1a;物體的加速度與所受合力成正比&#xff0c;與質量成反比&#xff0c;方向與合力方向相同。其基本矢量形式為&#xff1a; F?ma? \vec{F} m \vec{a} Fma 其中&#xff0c;F?\vec{F}F 是合力&#xff08;單…

【開發日記】SpringBoot 實現支持多個微信小程序的登錄

在實際業務場景中&#xff0c;需要一個后臺同時支持多個微信小程序的登錄。例如&#xff0c;企業有多個不同業務的小程序&#xff0c;但希望統一在同一個后臺系統里進行用戶認證和數據處理。這時候&#xff0c;我們就需要一個靈活的方式來管理多個小程序的 appid 和 secret&…

Docker 容器(一)

Docker一、Docker是什么1.什么是Docker2.Docker特點3.比較虛擬機和容器二、Docker安裝1.Docker??三大核心組件??2.安裝步驟&#xff08;Ubuntu&#xff09;3.阿里云鏡像加速三、Docker鏡像1.什么是鏡像2.UnionFS&#xff08;聯合文件系統&#xff09;3.Docker鏡像加載原理4…