面試題·棧和隊列的相互實現·詳解

A. 用隊列實現棧

用隊列實現棧
實現代碼如下
看著是隊列,其實實際實現更接近數組模擬

typedef struct {int* queue1;    // 第一個隊列int* queue2;    // 第二個隊列int size;       // 棧的大小int front1, rear1, front2, rear2;  // 兩個隊列的首尾指針
} MyStack;//初始化棧
MyStack* myStackCreate() {MyStack* stack = (MyStack*)malloc(sizeof(MyStack));stack->queue1 = (int*)malloc(sizeof(int) * 100);stack->queue2 = (int*)malloc(sizeof(int) * 100);stack->size = 0;stack->front1 = stack->rear1 = 0;stack->front2 = stack->rear2 = 0;return stack;
}//將元素 x 壓入棧頂 
void myStackPush(MyStack* obj, int x) {obj->queue1[obj->rear1++] = x;  // 將元素加入 queue1 的尾部obj->size++;
}//移除并返回棧頂元素 
int myStackPop(MyStack* obj) {int i;// 將 queue1 中的元素(除了最后一個)轉移到 queue2 中for (i = 0; i < obj->size - 1; i++) {obj->queue2[obj->rear2++] = obj->queue1[obj->front1++];}int top = obj->queue1[obj->front1++];  // 獲取棧頂元素obj->size--;// 交換 queue1 和 queue2,為下次操作做準備int* temp = obj->queue1;obj->queue1 = obj->queue2;obj->queue2 = temp;obj->front1 = 0;obj->rear1 = obj->size;obj->front2 = 0;obj->rear2 = 0;return top;
}//返回棧頂元素 
int myStackTop(MyStack* obj) {int i;// 將 queue1 中的元素轉移到 queue2 中for (i = 0; i < obj->size; i++) {obj->queue2[obj->rear2++] = obj->queue1[obj->front1++];}int top = obj->queue2[obj->rear2 - 1];  // 獲取棧頂元素// 交換 queue1 和 queue2,為下次操作做準備int* temp = obj->queue1;obj->queue1 = obj->queue2;obj->queue2 = temp;obj->front1 = 0;obj->rear1 = obj->size;obj->front2 = 0;obj->rear2 = 0;return top;
}//如果棧是空的,返回 true;否則,返回 false 
bool myStackEmpty(MyStack* obj) {return obj->size == 0;
}void myStackFree(MyStack* obj) {free(obj->queue1);free(obj->queue2);free(obj);
}

雖然注釋已經夠詳細了,但是我們還是來逐步分析一下
代碼實際上并沒有使用鏈式隊列,而是使用了兩個固定大小的數組來模擬棧的行為。

首先,我們定義了一個結構體MyStack,它包含兩個整數數組queue1和queue2(其實在這里,它們更像棧,因為數組是按照后進先出的順序使用的),以及它們的前后端指針和棧的大小。

myStackCreate

這個函數用于初始化棧。它分配了MyStack結構體的內存,并為兩個數組分配了內存。然后,它初始化了所有的指針和大小為0。

myStackPush
這個函數用于將元素壓入棧頂。它簡單地將元素添加到queue1的尾部,并更新rear1指針和棧的大小。

myStackPop
這個函數用于移除并返回棧頂元素。但是,這里有一個問題:它實際上將整個queue1(除了最后一個元素)轉移到了queue2,然后返回了queue1的最后一個元素。之后,它交換了兩個數組,并重置了所有的指針。這樣做在每次pop操作時都非常低效,因為它涉及到大量元素的移動。

一個更高效的方法是,每次pop時,只需要記錄queue1的最后一個元素(即棧頂元素),然后將其余元素(如果有的話)從queue1轉移到queue2,并交換兩個隊列的引用。但是,在交換后,不需要重置所有的指針,因為queue2的front2和rear2已經指向了正確的位置。

myStackTop
這個函數與myStackPop非常相似,但是它返回棧頂元素而不移除它。然而,與myStackPop一樣,它也涉及到了不必要的元素移動和數組交換。

myStackEmpty
這個函數檢查棧是否為空,通過檢查棧的大小是否為0來實現。

myStackFree
這個函數釋放了棧所使用的所有內存。

改進建議

  1. 避免不必要的元素移動:在pop和top操作中,只需要移動除了棧頂元素之外的其他元素。
  2. 減少指針重置:在交換數組后,不需要總是重置所有的前端和后端指針。
  3. 考慮使用真正的鏈式隊列:如果你想要一個鏈式棧,你應該使用鏈式隊列(通過節點連接)而不是數組。鏈式實現會更靈活,并且在處理大量數據時可能更高效。
  4. 錯誤處理:在分配內存時,應該檢查malloc是否成功,并在失敗時返回錯誤或進行其他處理。
  5. 邊界檢查:在pop和top操作中,應該檢查棧是否為空,以避免訪問空指針或未定義的內存。
  6. 優化內存使用:如果你知道棧的大小上限,可以使用固定大小的數組。但是,如果你想要一個可以動態增長的棧,那么鏈式實現可能更合適。

下面是優化后的代碼

在這個版本中,我們使用了一個指針queue和兩個布爾值(但實際上我們只需要一個,因為!isQueue1就等同于isQueue2)來追蹤當前正在使用的隊列。當棧滿時,我們嘗試擴容,并將元素從queue2(如果它包含元素)復制到queue1。我們還添加了一個檢查,以便在queue1使用了超過一半的空間時,將元素從queue1復制到queue2,并切換隊列,以保持兩個隊列之間的負載均衡。

我們還在myStackFree函數中釋放了內存,并處理了malloc和realloc可能返回NULL的情況。

#include <stdlib.h>  
#include <stdbool.h>  #define INITIAL_CAPACITY 100  typedef struct {  int* queue;  int capacity;  int top;  bool isQueue1; // 表示當前正在使用queue1還是queue2  
} MyStack;  MyStack* myStackCreate() {  MyStack* obj = (MyStack*)malloc(sizeof(MyStack));  if (!obj) return NULL;  obj->queue = (int*)malloc(INITIAL_CAPACITY * sizeof(int));  if (!obj->queue) {  free(obj);  return NULL;  }  obj->capacity = INITIAL_CAPACITY;  obj->top = -1; // 棧頂指針初始化為-1,表示空棧  obj->isQueue1 = true; // 初始時使用queue1  return obj;  
}  void myStackPush(MyStack* obj, int x) {  if (obj->top == obj->capacity - 1) { // 棧滿,擴容  int newCapacity = obj->capacity * 2;  int* newQueue = (int*)realloc(obj->isQueue1 ? obj->queue : (obj->queue - INITIAL_CAPACITY), newCapacity * sizeof(int));  if (!newQueue) return; // 擴容失敗  obj->queue = newQueue + (obj->isQueue1 ? 0 : INITIAL_CAPACITY); // 調整指針,確保obj->queue始終指向當前使用的隊列  obj->capacity = newCapacity;  // 如果之前使用queue2,現在切換回queue1,則需要復制數據  if (!obj->isQueue1) {  for (int i = 0; i <= obj->top; i++) {  obj->queue[i] = obj->queue[i + INITIAL_CAPACITY];  }  obj->isQueue1 = true; // 切換回queue1  }  }  obj->queue[++obj->top] = x; // 壓棧  
}  int myStackPop(MyStack* obj) {  if (obj->top == -1) return -1; // 棧空  int x = obj->queue[obj->top--]; // 彈出棧頂元素  // 如果queue1已經使用了超過一半的空間,且queue2是空的,則考慮將queue1的元素移到queue2,并切換隊列  if (obj->isQueue1 && obj->top > obj->capacity / 4) {  for (int i = 0; i <= obj->top; i++) {  obj->queue[i + INITIAL_CAPACITY] = obj->queue[i];  }  obj->isQueue1 = false; // 切換到queue2  obj->top += INITIAL_CAPACITY; // 更新top指針  }  return x;  
}  int myStackTop(MyStack* obj) {  if (obj->top == -1) return -1; // 棧空  return obj->queue[obj->top]; // 返回棧頂元素  
}  bool myStackEmpty(MyStack* obj) {  return obj->top == -1; // 檢查棧是否為空  
}  void myStackFree(MyStack* obj) {  if (obj) {  free(obj->queue - (obj->isQueue1 ? 0 : INITIAL_CAPACITY)); // 釋放隊列內存  free(obj);  }  
}

B. 用棧實現隊列

用棧實現隊列

那用鏈棧來實現一下看看

// 鏈表節點定義  
typedef struct Node {  int data;  struct Node* next;  
} Node;  // 棧結構定義
typedef struct Stack {  Node* top;  int size;  
} Stack;  // MyQueue結構體定義,包含兩個棧  
typedef struct MyQueue {  Stack* stack1;  Stack* stack2;  
} MyQueue;  
// 創建新的節點  
Node* createNode(int data) {  Node* newNode = (Node*)malloc(sizeof(Node));  if (!newNode) {  perror("Memory allocation failed");  exit(EXIT_FAILURE);  }  newNode->data = data;  newNode->next = NULL;  return newNode;  
}  // 初始化棧  
Stack* createStack() {  Stack* stack = (Stack*)malloc(sizeof(Stack));  if (!stack) {  perror("Memory allocation failed");  exit(EXIT_FAILURE);  }  stack->top = NULL;  stack->size = 0;  return stack;  
}  // 壓棧操作  
void push(Stack* stack, int data) {  Node* newNode = createNode(data);  newNode->next = stack->top;  stack->top = newNode;  stack->size++;  
}  // 出棧操作  
int pop(Stack* stack) {  if (stack->size == 0) {  printf("Stack is empty.\n");  exit(EXIT_FAILURE);  }  Node* temp = stack->top;  int data = temp->data;  stack->top = temp->next;  free(temp);  stack->size--;  return data;  
}  // 查看棧頂元素  
int peek(Stack* stack) {  if (stack->size == 0) {  printf("Stack is empty.\n");  exit(EXIT_FAILURE);  }  return stack->top->data;  
}  // 判斷棧是否為空  
int isEmpty(Stack* stack) {  return stack->size == 0;  
}  // 釋放棧內存  
void freeStack(Stack* stack) {  Node* current = stack->top;  Node* temp;  while (current != NULL) {  temp = current;  current = current->next;  free(temp);  }  free(stack);  
}MyQueue* myQueueCreate() {  MyQueue* queue = (MyQueue*)malloc(sizeof(MyQueue));  if (!queue) {  perror("Memory allocation failed for MyQueue");  exit(EXIT_FAILURE);  }  queue->stack1 = createStack();  queue->stack2 = createStack();  return queue;  
}  void myQueuePush(MyQueue* queue, int data) {  push(queue->stack1, data);  
}  int myQueuePop(MyQueue* queue) {  if (isEmpty(queue->stack2)) {  while (!isEmpty(queue->stack1)) {  push(queue->stack2, pop(queue->stack1));  }  }  if (isEmpty(queue->stack2)) {  return -1; // 隊列為空,無法彈出元素  }  return pop(queue->stack2);  
}  int myQueuePeek(MyQueue* queue) {  if (isEmpty(queue->stack2)) {  while (!isEmpty(queue->stack1)) {  push(queue->stack2, pop(queue->stack1));  }  }  if (isEmpty(queue->stack2)) {  return -1; // 隊列為空,無法查看隊首元素  }  return peek(queue->stack2);  
}  bool myQueueEmpty(MyQueue* queue) {  return isEmpty(queue->stack1) && isEmpty(queue->stack2);  
}  void myQueueFree(MyQueue* queue) {  freeStack(queue->stack1);  freeStack(queue->stack2);  free(queue);  
}

這段代碼實現了一個基于兩個棧的隊列結構 MyQueue。下面我們來逐步解釋這段代碼:

  1. 鏈表節點定義

    • Node 結構體定義了鏈表節點, 包含一個整數值 data 和指向下一個節點的指針 next
  2. 棧結構定義

    • Stack 結構體定義了一個棧, 包含指向棧頂元素的指針 top 和棧的大小 size
  3. MyQueue 結構體定義

    • MyQueue 結構體包含兩個 Stack 指針, stack1stack2, 用于實現隊列的功能。
  4. 創建新的節點:

    • createNode 函數用于創建一個新的鏈表節點, 并分配內存。如果內存分配失敗, 則輸出錯誤信息并退出程序。
  5. 初始化棧:

    • createStack 函數用于創建一個新的棧, 將棧頂指針設為 NULL, 并將棧的大小設為 0。如果內存分配失敗, 則輸出錯誤信息并退出程序。
  6. 壓棧操作:

    • push 函數用于將一個元素壓入棧, 首先創建一個新節點, 然后將新節點的 next 指針指向當前棧頂元素, 最后更新棧頂指針和棧的大小。
  7. 出棧操作:

    • pop 函數用于從棧中彈出一個元素, 首先檢查棧是否為空, 如果為空則輸出錯誤信息并退出程序。然后獲取棧頂元素的數據, 更新棧頂指針, 釋放棧頂元素的內存, 并更新棧的大小。
  8. 查看棧頂元素:

    • peek 函數用于查看棧頂元素, 首先檢查棧是否為空, 如果為空則輸出錯誤信息并退出程序。然后返回棧頂元素的數據。
  9. 判斷棧是否為空:

    • isEmpty 函數用于判斷棧是否為空, 返回棧的大小是否為 0。
  10. 釋放棧內存:

    • freeStack 函數用于釋放棧占用的內存, 遍歷鏈表并逐個釋放每個節點的內存, 最后釋放棧本身的內存。
  11. 創建 MyQueue:

    • myQueueCreate 函數用于創建一個新的 MyQueue 對象, 分配內存并初始化兩個 Stack 對象。如果內存分配失敗, 則輸出錯誤信息并退出程序。
  12. 入隊操作:

    • myQueuePush 函數用于將一個元素入隊, 直接將元素壓入 stack1 即可。
  13. 出隊操作:

    • myQueuePop 函數用于從隊列中出隊一個元素。首先檢查 stack2 是否為空, 如果為空則將 stack1 中的元素全部彈出并壓入 stack2。然后從 stack2 中彈出并返回隊首元素。如果兩個棧都為空, 則返回 -1 表示隊列為空。
  14. 查看隊首元素:

    • myQueuePeek 函數用于查看隊首元素。與出隊操作類似, 首先檢查 stack2 是否為空, 如果為空則將 stack1 中的元素全部彈出并壓入 stack2。然后返回 stack2 的棧頂元素。如果兩個棧都為空, 則返回 -1 表示隊列為空。
  15. 判斷隊列是否為空:

    • myQueueEmpty 函數用于判斷隊列是否為空, 返回 stack1stack2 是否同時為空。
  16. 釋放 MyQueue 內存:

    • myQueueFree 函數用于釋放 MyQueue 對象占用的內存, 首先釋放 stack1stack2 的內存, 然后釋放 MyQueue 對象本身的內存。

那么到這里,本篇文章就結束了,這兩題雖然沒有實用的意義,但是用來理解棧和隊列還是非常不錯的

期待與你的下次相見!!!
下期預告~二叉樹(上)-堆

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

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

相關文章

圖像處理ASIC設計方法 筆記25 紅外成像技術:未來視覺的革命

在當今科技飛速發展的時代,紅外成像技術以其獨特的優勢,在醫療、工業檢測等多個領域扮演著越來越重要的角色。本章節(P146 第7章紅外焦平面非均勻性校正SoC)將深入探討紅外成像系統中的關鍵技術——非均勻性校正SoC,以及它如何推動紅外成像技術邁向新的高度。 紅外成像系統…

6.Redis之String命令

1.String類型基本介紹 redis 所有的 key 都是字符串, value 的類型是存在差異的~~ 一般來說,redis 遇到亂碼問題的概率更小~~ Redis 中的字符串,直接就是按照二進制數據的方式存儲的. (不會做任何的編碼轉換【講 mysql 的時候,知道 mysql 默認的字符集, 是拉丁文,插入中文…

Jenkins--從入門到入土

Jenkins–從入門到入土 文章目錄 Jenkins--從入門到入土〇、概念提要--什么是CI/DI&#xff1f;1、CI&#xff08;Continuous Integration&#xff0c;持續集成&#xff09;2、DI&#xff08;DevOps Integration&#xff0c;DevOps 集成&#xff09;3、解決的問題 一、Jenkins安…

iOS 開發系列:基于VNRecognizeTextRequest識別圖片文字

1.添加Vision Kit依賴 在項目設置中點擊"General"選項卡&#xff0c;然后在"Frameworks, Libraries, and Embedded Content"&#xff08;框架、庫和嵌入內容&#xff09;部分&#xff0c;點擊""按鈕。搜索并選擇"Vision.framework"。…

[AIGC] flink sql 消費kafka消息,然后寫到mysql中的demo

這是一個使用 Flink SQL 從 Kafka 中消費數據并寫入 MySQL 的示例。在這個示例中&#xff0c;我們將假設有一個 Kafka 主題 “input_topic”&#xff0c;它產生格式為 (user_id: int, item_id: int, behavior: string, timestamp: long) 的數據&#xff0c;我們需要把這些數據寫…

world machine學習筆記(4)

選擇設備&#xff1a; select acpect&#xff1a; heading&#xff1a;太陽的方向 elevation&#xff1a;太陽的高度 select colour&#xff1a;選擇顏色 select convexity&#xff1a;選擇突起&#xff08;曲率&#xff09; select height&#xff1a;選擇高度 falloff&a…

用常識滾雪球:拼多多的內生價值,九年的變與不變

2024年5月22日&#xff0c;拼多多公布了今年一季度財報&#xff0c;該季度拼多多集團營收868.1億元&#xff0c;同比增長131%&#xff0c;利潤306.0億&#xff0c;同比增長了202%&#xff0c;數據亮眼。 市場對拼多多經歷了“看不見”、“看不懂”、“跟不上”三個階段。拼多多…

Vue.js條件渲染與列表渲染指南

title: Vue.js條件渲染與列表渲染指南 date: 2024/5/26 20:11:49 updated: 2024/5/26 20:11:49 categories: 前端開發 tags: VueJS前端開發數據綁定列表渲染狀態管理路由配置性能優化 第1章&#xff1a;Vue.js基礎與環境設置 1.1 Vue.js簡介 Vue.js (讀音&#xff1a;/vju…

SwiftUI中的Slider的基本使用

在SwiftUI中&#xff0c;可以使用Slider視圖創建一個滑動條&#xff0c;允許用戶從范圍中選擇一個值。通過系統提供的Slider&#xff0c;用起來也很方便。 Slider 先看一個最簡單的初始化方法&#xff1a; State private var sliderValue: Float 100var body: some View {V…

[AIGC] mac os 中 .DS_Store 是什么

.DS_Store 是在 MacOS 系統中由 Finder 應用程序創建和維護的一種隱藏文件&#xff0c;用于保存有關其所在目錄的自定義屬性&#xff0c;例如圖標位置或背景顏色。 “.DS_Store” 是 “Desktop Services Store” 的縮寫。 .DS_Store 的作用 .DS_Store 文件在每個 Mac OS X 文…

ollama 使用,以及指定模型下載地址

ollama windows 使用 官網&#xff1a; https://ollama.com/ windows 指定 models 下載地址 默認會下載在C盤 &#xff0c;占用空間 在Windows系統中&#xff0c;可以通過設置環境變量OLLAMA_MODELS來指定模型文件的下載和存儲路徑。具體操作步驟如下&#xff1a; 1.打開系統…

【python006】miniconda3環境搭建(非root目錄,最近更新中)

1.熟悉、梳理、總結項目研發實戰中的Python開發日常使用中的問題。 2.歡迎點贊、關注、批評、指正&#xff0c;互三走起來&#xff0c;小手動起來&#xff01; 文章目錄 1.背景介紹2. 1.背景介紹 環境移植&#xff0c;可能影響部署本機環境信息&#xff0c;探索、總結移植有效…

輕量化微調相關學習

輕量化微調&#xff08;Lightweight Fine-Tuning&#xff09;是指在大型預訓練模型基礎上&#xff0c;通過修改或添加少量參數來進行模型適應性調整的一種方法&#xff0c;旨在減少計算資源消耗和避免過擬合問題&#xff0c;同時保持模型的性能。這種方法特別適用于資源有限或需…

一個程序員的牢獄生涯(36)夾帶

星期一 夾 帶 鄭所和小X州在小院子里說著話,盡管我豎起耳朵想要聽到他們的說話內容。但因為他們的說話聲音很低,我努力半天后,什么都聽不清。只能看到小X州恭恭敬敬的站在鄭所面前,不時地點頭答應著的樣子。 沒過多長時間,小X州從院子里返回了號子。我注意到他的臉上帶著一…

15、設計模式之責任鏈模式

責任鏈模式 顧名思義&#xff0c;責任鏈模式&#xff08;Chain of Responsibility Pattern&#xff09;為請求創建了一個接收者對象的鏈。這種模式給予請求的類型&#xff0c;對請求的發送者和接收者進行解耦。這種類型的設計模式屬于行為型模式。 在這種模式中&#xff0c;通…

搜索引擎--ES基礎概念

ES是一款開源的搜索引擎&#xff0c;相比于mysql&#xff0c;它提供了非常強大的搜索功能 下面我們需要簡單的了解一下ES相比于mysql中的一些基本概念的區別&#xff1a; 首先我們要知道es在存儲數據的時候都是以json格式來存儲的 mysql <------> ES&#xff1a; table…

【九十四】【算法分析與設計】練習四蠻力法練習,排列問題和組合問題,求解最大連續子序列和問題,求解冪集問題,求解0/1背包問題,求解任務分配問題

求解最大連續子序列和問題 給定一個有n&#xff08;n≥1&#xff09;個整數的序列&#xff0c;要求求出其中最大連續子序列的和。 例如&#xff1a; 序列&#xff08;-2&#xff0c;11&#xff0c;-4&#xff0c;13&#xff0c;-5&#xff0c;-2&#xff09;的最大子序列和為20…

pymysql.err.OperationalError: (1030, ‘Got error 168 from storage engine‘)

錯誤 pymysql.err.OperationalError: (1030, Got error 168 from storage engine) 通常與MySQL的InnoDB存儲引擎相關&#xff0c;它指示你試圖進行的操作超出了存儲引擎的能力或資源限制。具體來說&#xff0c;MySQL錯誤代碼168&#xff08;或“ER_TABLE_NEEDS_UPGRADE”&#…

[處理器芯片]-6 超標量CPU實現之浮點運算

1 浮點運算單元FPU 超標量CPU中的浮點運算單元是專門處理浮點數運算的關鍵組件。浮點運算單元的設計涉及多個復雜的子模塊和技術&#xff0c;以保證高效、準確地執行浮點數的加減法、乘法、除法、平方根等操作。 1&#xff09;浮點數術語 浮點數通常采用IEEE 754標準表示&…

顯示IPS技術

顯示器的IPS&#xff08;In-Plane Switching&#xff0c;平面轉換&#xff09;技術是一種先進的液晶面板技術&#xff0c;由日立公司在2001年推出。該技術優化了液晶分子的排列方式&#xff0c;采取水平排列&#xff0c;使得分子結構在遇到外界壓力時仍能保持穩定&#xff0c;不…