【數據結構】棧的應用

目錄

0? 引言

1? 棧在括號匹配中的應用

2? 棧在表達式求值中的應用

? ? ? ? 2.1 算數表達式

? ? ? ? 2.2 中綴表達式轉后綴表達式

2.3 后綴表達式求值

3? 棧在遞歸中的應用

3.1 棧在函數調用中的作用

3.2 棧在函數調用中的工作原理

4? 總結


0? 引言

????????棧(Stack)是一種非常基本且重要的數據結構,它們在許多計算機科學和軟件工程的應用中都有廣泛的用途。

? ? ? ? 棧:

????????????????①括號匹配;

? ? ? ? ? ? ? ? ②表達式求值;

? ? ? ? ? ? ? ? ③遞歸函數調用。

1? 棧在括號匹配中的應用

? ? ? ? 表達式中有兩種括號:圓括號 ( ) 和 方括號 [ ],嵌套的順序任意,但應為正確的格式。

????????例如:( ( [ ]?[ ] ) ) 為正確格式。

? ? ? ? 但如何用算法實現括號匹配問題?

? ? ? ? 思路如下:

? ? ? ? (1)初始一個空棧;

? ? ? ? (2)順序讀入括號;

? ? ? ? (3)當讀入的為左括號,將繼續讀入括號,直到讀入第一個右括號。那將檢測與之最近的左括號是否與之相匹配,若匹配,則出棧;若不匹配,則退出程序。當程序結束時,棧為空。反之,則表明括號序列的格式不正確。

? ? ? ? 代碼如下:

#include <stdio.h>  
#include <stdlib.h>  
#include <stdbool.h>  #define MAX_SIZE 100 // 假設棧的最大大小  typedef struct {  char data[MAX_SIZE];  int top;  
} Stack;  // 初始化棧  
void initStack(Stack *s) {  s->top = -1;  
}  // 判斷棧是否為空  
bool isEmpty(Stack *s) {  return s->top == -1;  
}  // 入棧  
void push(Stack *s, char c) {  if (s->top >= MAX_SIZE - 1) {  printf("Stack overflow\n");  return;  }  s->data[++s->top] = c;  
}  // 出棧  
char pop(Stack *s) {  if (isEmpty(s)) {  printf("Stack underflow\n");  return '#'; // 返回一個無效字符,或可以選擇拋出一個錯誤  }  return s->data[s->top--];  
}  // 檢查兩個括號是否匹配  
bool isMatch(char c1, char c2) {  if (c1 == '(' && c2 == ')') return true;  if (c1 == '[' && c2 == ']') return true;  if (c1 == '{' && c2 == '}') return true;  return false;  
}  // 括號匹配函數  
bool isBalanced(char *str) {  Stack s;  initStack(&s);  for (int i = 0; str[i] != '\0'; i++) {  if (str[i] == '(' || str[i] == '[' || str[i] == '{') {  push(&s, str[i]);  } else if (str[i] == ')' || str[i] == ']' || str[i] == '}') {  if (isEmpty(&s)) {  // 棧為空,但遇到了右括號,不匹配  return false;  }  char topChar = pop(&s);  if (!isMatch(topChar, str[i])) {  // 棧頂元素與當前右括號不匹配  return false;  }  }  }  // 如果棧為空,則所有括號都匹配  return isEmpty(&s);  
}  int main() {  char str[MAX_SIZE];  printf("Enter a string with brackets: ");  scanf("%s", str); if (isBalanced(str)) {  printf("The brackets are balanced.\n");  } else {  printf("The brackets are not balanced.\n");  }  return 0;  
}

2? 棧在表達式求值中的應用

? ? ? ? 2.1 算數表達式

????????中綴表達式是人們常用的算術表達式,即操作符以中綴形式處于操作數之間。但在計算機中,中綴表達式相較于前綴和后綴表達式來說,更不易被計算機識別。前綴表達式成為波蘭式,后綴表達式又稱逆波蘭式。

? ? ? ? 2.2 中綴表達式轉后綴表達式

? ? ? ? (1)手算方法:

? ? ? ? ①根據運算順序對表達式運算符排號;

? ? ? ? ②根據運算符排號順序,將運算符及兩端的操作數以(左操作數 右操作數 運算符)的順序重新組合。

? ? ? ? 例如:( A + B ) * C + ( D - E ) / F 轉后綴表達式的過程如下:

? ? ? ??(2)算法實現:

? ? ? ? ①初始一個棧;

? ? ? ? ②遇到操作數,直接加入后綴表達式;

? ? ? ? ③遇到界限符,若為左括號直接入棧,若為右括號,則依次彈出棧中的運算符,加入后綴表達式,知道彈出左括號為止。需要注意的是,左括號和右括號直接刪除,不加入后綴表達式。

? ? ? ? ④遇到運算符,則看運算符的優先級,若高于除左括號外的棧頂元素,則直接入棧。反之,則依次彈出棧中優先級高于或等于當前運算符的所有運算符,并加入后綴表達式,直到遇到低于他的優先級的運算符,才入棧。

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <stdbool.h>  #define MAX_SIZE 100  typedef struct {  char data[MAX_SIZE];  int top;  
} Stack;  // 初始化棧  
void initStack(Stack *s) {  s->top = -1;  
}  // 判斷棧是否為空  
bool isEmpty(Stack *s) {  return s->top == -1;  
}  // 入棧  
bool push(Stack *s, char c) {  if (s->top >= MAX_SIZE - 1) {  return false; // 棧溢出  }  s->data[++s->top] = c;  return true;  
}  // 出棧  
char pop(Stack *s) {  if (isEmpty(s)) {  return '\0'; // 棧空,返回空字符  }  return s->data[s->top--];  
}  // 獲取棧頂元素,但不彈出  
char peek(Stack *s) {  if (isEmpty(s)) {  return '\0'; // 棧空,返回空字符  }  return s->data[s->top];  
}  // 運算符的優先級比較(這里只處理了基本的四則運算)  
int precedence(char op) {  if (op == '+' || op == '-') {  return 1;  }  if (op == '*' || op == '/') {  return 2;  }  return 0; // 如果不是運算符,返回0  
}  // 將中綴表達式轉換為后綴表達式  
void infixToPostfix(char *infix, char *postfix) {  Stack s;  initStack(&s);  int i = 0, j = 0;  while (infix[i] != '\0') {  if (infix[i] >= '0' && infix[i] <= '9') {  // 如果是操作數,直接添加到后綴表達式中  postfix[j++] = infix[i++];  postfix[j++] = ' '; // 假設操作數都是個位數,用空格分隔  } else if (infix[i] == '(') {  // 如果是左括號,直接入棧  push(&s, infix[i++]);  } else if (infix[i] == ')') {  // 如果是右括號,則彈出棧中元素直到遇到左括號  while (!isEmpty(&s) && peek(&s) != '(') {  postfix[j++] = pop(&s);  postfix[j++] = ' ';  }  // 彈出左括號,但不加入后綴表達式  pop(&s);  i++;  } else {  // 如果是運算符  while (!isEmpty(&s) && precedence(peek(&s)) >= precedence(infix[i])) {  // 如果棧不為空且棧頂元素優先級高于或等于當前運算符,彈出棧頂元素  postfix[j++] = pop(&s);  postfix[j++] = ' ';  }  // 當前運算符入棧  push(&s, infix[i++]);  }  }  // 彈出棧中剩余的所有運算符  while (!isEmpty(&s)) {  postfix[j++] = pop(&s);  postfix[j++] = ' ';  }  // 添加字符串結束符  postfix[j] = '\0';  
}  int main() {  char infix[MAX_SIZE], postfix[MAX_SIZE * 2]; // 后綴表達式可能更長,因此分配更多空間  printf("Enter an infix expression: ");  scanf("%s", infix); // 注意:這里不會處理空格和復雜輸入  infixToPostfix(infix, postfix);  printf("Postfix expression: %s\n", postfix);  return 0;  
}

2.3 后綴表達式求值

????????后綴表達式(也稱為逆波蘭表示法或逆波蘭記法)是一種不需要括號來標明運算符的優先級的數學表達式。在這種表示法中,所有的運算符都放在操作數的后面。

????????求值后綴表達式的基本步驟如下:

  • 初始化一個棧,用于存儲操作數。
  • 從左到右掃描后綴表達式。
  • 如果掃描到操作數,則將其壓入棧中。
  • 如果掃描到運算符,則從棧中彈出兩個操作數(先彈出的為右操作數,后彈出的為左操作數),將這兩個操作數作為運算符的輸入進行運算,然后將結果壓回棧中。
  • 重復步驟2-4,直到后綴表達式掃描完畢。
  • 棧中剩下的元素就是表達式的值。

????????示例

????????后綴表達式:3 4 + 5 *

????????求值過程:

  1. 掃描到?3,壓入棧:[3]
  2. 掃描到?4,壓入棧:[3, 4]
  3. 掃描到?+,彈出?4?和?3,計算?3 + 4?得到?7,壓入棧:[7]
  4. 掃描到?5,壓入棧:[7, 5]
  5. 掃描到?*,彈出?5?和?7,計算?7 * 5?得到?35,壓入棧:[35]
  6. 掃描完畢,棧中元素?35?即為表達式的值。

????????下面是實現代碼(以上述示例為例):

#include <stdio.h>  
#include <stdlib.h>  
#include <ctype.h>  
#include <string.h>  #define MAX_STACK_SIZE 100  typedef struct {  double data[MAX_STACK_SIZE];  int top;  
} Stack;  // 初始化棧  
void initStack(Stack *s) {  s->top = -1;  
}  // 判斷棧是否為空  
int isEmpty(Stack *s) {  return s->top == -1;  
}  // 壓棧操作  
void push(Stack *s, double value) {  if (s->top >= MAX_STACK_SIZE - 1) {  printf("Stack overflow\n");  exit(1);  }  s->data[++s->top] = value;  
}  // 彈棧操作  
double pop(Stack *s) {  if (isEmpty(s)) {  printf("Stack underflow\n");  exit(1);  }  return s->data[s->top--];  
}  // 求值后綴表達式  
double evaluatePostfix(const char *postfix) {  Stack s;  initStack(&s);  const char *token = strtok((char *)postfix, " "); // 假設操作符和操作數之間用空格分隔  while (token != NULL) {  if (isdigit(token[0])) { // 如果是操作數  double value = atof(token);  push(&s, value);  } else { // 如果是運算符  double rightOperand = pop(&s); // 彈出右操作數  double leftOperand = pop(&s); // 彈出左操作數  switch (token[0]) {  case '+':  push(&s, leftOperand + rightOperand);  break;  case '-':  push(&s, leftOperand - rightOperand);  break;  case '*':  push(&s, leftOperand * rightOperand);  break;  case '/':  if (rightOperand != 0.0) {  push(&s, leftOperand / rightOperand);  } else {  printf("Error: Division by zero\n");  exit(1);  }  break;  default:  printf("Error: Unknown operator\n");  exit(1);  }  }  token = strtok(NULL, " "); // 繼續獲取下一個token  }  if (!isEmpty(&s)) {  return pop(&s); // 棧中剩下的元素就是表達式的值  } else {  printf("Error: Invalid postfix expression\n");  exit(1);  }  
}  int main() {  const char *postfix = "3 4 + 5 *";  double result = evaluatePostfix(postfix);  printf("Result: %lf\n", result);  return 0;  
}

3? 棧在遞歸中的應用

3.1 棧在函數調用中的作用

  • 參數傳遞:當調用一個函數時,需要傳遞參數給該函數。這些參數會被壓入棧中,以便函數內部能夠訪問和使用它們。
  • 局部變量分配:函數內部定義的局部變量會在棧上分配空間。這些變量的生命周期與函數的執行周期相同,當函數執行完畢后,這些局部變量所占用的棧空間會被自動釋放。
  • 保存調用的返回地址:在函數調用時,CPU需要知道函數執行完畢后應該返回到哪個位置繼續執行。這個返回地址會被保存在棧中,以便函數執行完畢后能夠正確地返回到調用它的位置。
  • 保存寄存器以供恢復:在函數調用和返回的過程中,CPU的寄存器狀態會發生變化。為了能夠在函數返回后恢復原來的寄存器狀態,棧會保存這些寄存器的值。

3.2 棧在函數調用中的工作原理

  • 函數調用:當調用一個函數時,系統首先會創建一個新的棧幀(stack frame)來保存該函數的執行環境。這個棧幀包含了函數的返回地址、參數、局部變量等信息。然后,系統會將當前程序的執行狀態(如返回地址、寄存器狀態等)壓入棧中,以便在函數執行完畢后能夠恢復。
  • 函數執行:在函數執行過程中,函數會訪問棧幀中的參數和局部變量,并根據需要進行計算和操作。同時,如果函數內部調用了其他函數,系統也會為這些被調用的函數創建新的棧幀,并將當前函數的執行狀態壓入棧中保存。
  • 函數返回:當函數執行完畢或者遇到return語句時,系統會彈出當前函數的棧幀,并根據棧幀中的返回地址返回到調用它的位置繼續執行。在返回之前,系統還會恢復調用該函數時的寄存器狀態。

? ? ? ? 下面將給出一個例子:

? ? ? ? 例如:階乘,大家可以自行調試;

#include <stdio.h>int step(int n){if(n==1)return 1;elsereturn n*step(n-1);
}int main(){int n,s;scanf("%d",&n);s=step(n);printf("%d",s);
}

4? 總結

????????在本文中,我們深入探討了棧這一數據結構及其在各種應用場景中的重要作用。棧作為一種后進先出(LIFO)的數據結構,其獨特的操作方式——壓棧(push)和彈棧(pop),使得它在計算機科學和軟件開發中占據了不可或缺的地位。

????????詳細討論了棧在多個領域中的應用。其中,后綴表達式的求值是一個經典的棧應用示例。在這個問題中,我們利用棧來存儲操作數,并通過操作數的彈出和結果的壓入,實現了表達式的正確計算。這種方法不僅簡化了表達式的處理流程,而且提高了計算效率。

????????此外,棧還在函數調用、遞歸等方面發揮著重要作用。在函數調用中,棧用于存儲局部變量和返回地址,確保函數能夠正確地返回并繼續執行。在遞歸算法中,棧用于保存遞歸調用的中間結果,從而避免重復計算。

????????綜上所述,棧作為一種基本而強大的數據結構,在各個領域都有著廣泛的應用。通過學習和掌握棧的使用方法和應用場景,我們能夠更好地解決實際問題,提高編程效率。

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

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

相關文章

MySQL A表的字段值更新為B表的字段值

MySQL A表的字段值更新為B表的字段值 準備數據表 create table person (id int unsigned auto_increment comment 主鍵 primary key,uuid varchar(32) not null comment 系統唯一標識符32個長度的字符串,mobile varchar(11) null comment 中國國內手機號,nickn…

使用 Ollama 和 Open WebUI 自托管 LLM 聊天機器人(無需 GPU)

?點擊這里?&#xff1a;&#x1f680;原文鏈接&#xff1a;&#xff08;更好排版、視頻播放、社群交流、最新AI開源項目、AI工具分享都在這個公眾號&#xff01;&#xff09; 使用 Ollama 和 Open WebUI 自托管 LLM 聊天機器人&#xff08;無需 GPU&#xff09; &#x1f31…

二叉查找樹詳解

目錄 二叉查找樹的定義 二叉查找樹的基本操作 查找 插入 建立 刪除 二叉樹查找樹的性質 二叉查找樹的定義 二叉查找樹是一種特殊的二叉樹&#xff0c;又稱為排序二叉樹、二叉搜索樹、二叉排序樹。 二叉樹的遞歸定義如下&#xff1a; &#xff08;1&#xff09;要么二…

10. MySQL 用戶

文章目錄 【 1. 權限表 】1.1 user 權限表1.1.1 用戶列1.1.2 權限列1.1.3 安全列1.1.4 資源控制列 1.2 db 表用戶列權限列 1.3 tables_priv 表1.4 columns_priv 表1.5 procs_priv表 【 2. 用戶管理 】2.1 創建用戶 CREATE USER2.2 用戶的登陸、退出登陸 MySQL退出 MySQL 2.3 重…

Java常見錯誤-內部類-簡要分析

Java常見錯誤-內部類-簡要分析 概念分類成員內部類&#xff08;非靜態內部類&#xff09;靜態內部類成員內部類和靜態內部類區別 局部內部類匿名內部類 注意事項總結 概念 ? 內部類&#xff0c;顧名思義&#xff0c;就是在一個類的內部定義的類。這種設計允許將一個類的實現細…

深度學習-10-測試

深度學習-10-測試 本文是《深度學習入門2-自製框架》 的學習筆記&#xff0c;記錄自己學習心得&#xff0c;以及對重點知識的理解。如果內容對你有幫助&#xff0c;請支持正版&#xff0c;去購買正版書籍&#xff0c;支持正版書籍不僅是尊重作者的辛勤勞動&#xff0c;也是鼓勵…

Web前端ES6-ES13筆記合集(下)

#### 五.ES10新特性 ##### 1. Object.fromEntries > Object.fromEntries()方法允許你輕松地將鍵值對列表轉換為對象 js const arr [["name", "kerwin"], ["age", 100]]; console.log(Object.fromEntries(arr))//{name: kerwin, age: 100} …

pytorch 筆記:pytorch 優化內容(更新中)

1 Tensor創建類 1.1 直接創建Tensor&#xff0c;而不是從Python或Numpy中轉換 不要使用原生Python或NumPy創建數據&#xff0c;然后將其轉換為torch.Tensor直接用torch.Tensor創建或者直接&#xff1a;torch.empty(), torch.zeros(), torch.full(), torch.ones(), torch.…

樹莓派【Raspberry Pi-64位】3b+,Pi4J 2.0入門

一.前言: 前面的文章講解了樹莓派在centos7 arm64版本下的使用,用一款智能小車為例子,做了代碼實踐。 由于centos7不再維護,且Pi4J 1.x版本也因為WiringPi 的局限,Pi4J從1.x升級為2.x.所以本專欄的技術棧也將進行調整,A.從centos7系統回到Raspberry Pi-64位系統。B.Pi4…

4.通用編程概念

目錄 一、變量與常量1.1 變量1.2 常量 二、遮蔽三、數據類型3.1 標量類型1. 整型2. 浮點型3. 布爾類型4.字符類型 3.2 復合類型1. 元組2. 數組 四、函數五、語句和表達式六、函數的返回值 一、變量與常量 1.1 變量 在Rust中默認的變量是不可變的&#xff0c;如果修改其值會導致…

《青少年編程與數學》課程方案:4、課程策略

《青少年編程與數學》課程方案&#xff1a;4、課程策略 一、工程師思維二、使命感驅動三、價值觀引領四、學習現代化五、工作生活化六、與時代共進 《青少年編程與數學》課程策略強調采用工程師思維&#xff0c;避免重復造輪子&#xff0c;培養使命感&#xff0c;通過探索興趣、…

編程語言有哪些?這些希望你都知道

編程語言有哪些 編程語言有很多種&#xff0c;包括但不限于以下幾種&#xff1a; Java&#xff1a;當今最普遍使用的開發語言之一&#xff0c;簡單易學&#xff0c;且跨平臺性非常強&#xff0c;對網絡開發的支持令人稱贊。Python&#xff1a;語法清楚&#xff0c;干凈&#…

【Vue】如何提供訪問vuex的數據

文章目錄 一、提供數據二、訪問Vuex中的數據通過$store訪問的語法1&#xff09;模板中使用2&#xff09;組件邏輯中使用3&#xff09;js文件中使用 三、通過輔助函數 - mapState獲取 state中的數據 一、提供數據 State提供唯一的公共數據源&#xff0c;所有共享的數據都要統一…

[office] 快速刪除excel中的空行和列的方法 #其他#學習方法#經驗分享

快速刪除excel中的空行和列的方法 用戶在網上下載好的Excel表格打開之后發現有很多空白行&#xff0c;怎么樣將這些空白行或單元格一次性刪除掉呢?下面教大家在Excel中用定位一次性可以把空白行刪除 用戶在網上下載好的Excel表格打開之后發現有很多空白行&#xff0c;怎么樣將…

Vue3 使用audio播放語音+監聽播放語音完成事件

需求&#xff1a;輸入一段文字&#xff0c;點擊語音框&#xff0c;將本地語音&#xff08;提前準備好的&#xff09; 播放出來 播放中效果 代碼 <div class"listConAI" click"handleOpenSpeech" ><imgsrc"../../../../assets/images/blueo…

web前端 孫俏:深度探索與實戰之路

web前端 孫俏&#xff1a;深度探索與實戰之路 在這個數字化、信息化的時代&#xff0c;web前端技術以其獨特的魅力&#xff0c;吸引著越來越多的開發者投身其中。今天&#xff0c;我們將跟隨孫俏的腳步&#xff0c;一同探索web前端的深度與廣度&#xff0c;揭開其神秘的面紗。…

中文文案寫作有哪些合適的AIGC工具?

這是計育韜老師第 8 次開展面向全國高校的新媒體技術公益巡講活動了。而在每場講座尾聲&#xff0c;互動答疑環節往往反映了高校師生當前最普遍的運營困境&#xff0c;特此計老師在現場即興答疑之外&#xff0c;會盡量選擇有較高價值的提問進行文字答疑梳理。 *本輪巡講主題除了…

【Vue】開啟嚴格模式及Vuex的單項數據流

文章目錄 一、引出問題二、開啟嚴格模式 一、引出問題 目標 明確 vuex 同樣遵循單向數據流&#xff0c;組件中不能直接修改倉庫的數據 這樣數據的流向才會更加清晰&#xff0c;將來對數據的修改&#xff0c;都在倉庫內部實現的&#xff0c;更易于維護 直接在組件中修改Vuex中…

Git:版本控制的藝術與GitLab實戰指南

在當今快速發展的軟件開發領域&#xff0c;高效、協同的代碼管理是項目成功的關鍵。Git&#xff0c;作為一款分布式版本控制系統&#xff0c;憑借其強大的功能和靈活性&#xff0c;成為了眾多開發者首選的版本控制工具。本文將帶您深入探索Git的核心概念、基礎操作&#xff0c;…

B3870 [GESP202309 四級] 變長編碼

[GESP202309 四級] 變長編碼 題目描述 小明剛剛學習了三種整數編碼方式&#xff1a;原碼、反碼、補碼&#xff0c;并了解到計算機存儲整數通常使用補碼。但他總是覺得&#xff0c;生活中很少用到 2 31 ? 1 2^{31}-1 231?1 這么大的數&#xff0c;生活中常用的 0 ~ 100 0…