C语言如何实现栈:使用数组、使用链表、实现细节
实现栈是C语言中的一个经典问题,可以通过数组实现栈、可以通过链表实现栈、需要了解栈的基本操作。本文将详细介绍如何在C语言中实现栈,着重讲解使用数组和链表这两种方法,并探讨具体的实现细节和注意事项。
一、栈的基本概念
在计算机科学中,栈是一种后进先出(LIFO,Last In First Out)的数据结构。栈的基本操作包括以下几个:
- 压栈(Push):将元素添加到栈顶。
- 弹栈(Pop):从栈顶移除元素。
- 查看栈顶元素(Peek):查看栈顶的元素但不移除它。
- 检查栈是否为空(IsEmpty):判断栈中是否有元素。
- 检查栈是否已满(IsFull):判断栈是否达到容量上限(仅限于使用数组实现的栈)。
栈的实际应用
栈在很多场景中都有广泛应用,例如:
- 函数调用管理:在程序运行时,函数调用的管理通常使用栈来保存函数调用信息。
- 表达式求值:在计算器或者编译器中,栈常用于中缀表达式转后缀表达式的过程中。
- 撤销操作:在文档编辑等场景中,栈可以用于实现撤销(Undo)功能。
二、使用数组实现栈
1. 初始化栈
使用数组实现栈的一个简单方法是定义一个固定大小的数组,并使用一个变量来记录栈顶的位置。以下是栈的初始化代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_SIZE 100
typedef struct {
int items[MAX_SIZE];
int top;
} Stack;
void initialize(Stack* s) {
s->top = -1;
}
在这个实现中,MAX_SIZE
定义了栈的最大容量,top
变量记录了栈顶的位置。初始化时,将top
设置为-1,表示栈为空。
2. 压栈操作(Push)
压栈操作将新元素添加到栈顶。如果栈已满,压栈操作将失败。以下是压栈操作的实现代码:
bool push(Stack* s, int value) {
if (s->top == MAX_SIZE - 1) {
printf("Stack overflown");
return false;
}
s->items[++(s->top)] = value;
return true;
}
在这个实现中,首先检查栈是否已满。如果栈未满,将新元素添加到top
的位置,并将top
增加1。
3. 弹栈操作(Pop)
弹栈操作从栈顶移除元素。如果栈为空,弹栈操作将失败。以下是弹栈操作的实现代码:
bool pop(Stack* s, int* value) {
if (s->top == -1) {
printf("Stack underflown");
return false;
}
*value = s->items[(s->top)--];
return true;
}
在这个实现中,首先检查栈是否为空。如果栈不为空,将栈顶元素赋值给传入的变量,并将top
减少1。
4. 查看栈顶元素(Peek)
查看栈顶元素的操作不会移除元素,只是读取栈顶的值。以下是查看栈顶元素的实现代码:
bool peek(Stack* s, int* value) {
if (s->top == -1) {
printf("Stack is emptyn");
return false;
}
*value = s->items[s->top];
return true;
}
5. 检查栈是否为空(IsEmpty)
判断栈是否为空,可以通过检查top
变量是否为-1来实现:
bool isEmpty(Stack* s) {
return s->top == -1;
}
6. 检查栈是否已满(IsFull)
判断栈是否已满,可以通过检查top
变量是否等于MAX_SIZE - 1
来实现:
bool isFull(Stack* s) {
return s->top == MAX_SIZE - 1;
}
三、使用链表实现栈
与数组不同,链表实现的栈没有容量限制,可以根据需要动态地增加或减少元素。链表实现的栈通常由一系列节点组成,每个节点包含栈中的一个元素和指向下一个节点的指针。
1. 定义节点结构
首先定义节点结构,节点包含数据和指向下一个节点的指针:
typedef struct Node {
int data;
struct Node* next;
} Node;
2. 初始化栈
使用链表实现的栈需要一个指向栈顶节点的指针:
typedef struct {
Node* top;
} Stack;
void initialize(Stack* s) {
s->top = NULL;
}
3. 压栈操作(Push)
压栈操作在链表实现的栈中,需要创建一个新节点,并将其添加到链表头部:
bool push(Stack* s, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
if (newNode == NULL) {
printf("Memory allocation failedn");
return false;
}
newNode->data = value;
newNode->next = s->top;
s->top = newNode;
return true;
}
4. 弹栈操作(Pop)
弹栈操作在链表实现的栈中,需要移除链表头部的节点:
bool pop(Stack* s, int* value) {
if (s->top == NULL) {
printf("Stack underflown");
return false;
}
Node* temp = s->top;
*value = temp->data;
s->top = s->top->next;
free(temp);
return true;
}
5. 查看栈顶元素(Peek)
查看栈顶元素的操作在链表实现的栈中,需要读取链表头部节点的数据:
bool peek(Stack* s, int* value) {
if (s->top == NULL) {
printf("Stack is emptyn");
return false;
}
*value = s->top->data;
return true;
}
6. 检查栈是否为空(IsEmpty)
判断栈是否为空,可以通过检查top
指针是否为NULL
来实现:
bool isEmpty(Stack* s) {
return s->top == NULL;
}
四、栈的综合应用
1. 表达式求值
使用栈可以实现表达式的求值,例如中缀表达式转后缀表达式,并计算后缀表达式的值。以下是一个简单的例子:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
int evaluatePostfix(const char* expression) {
Stack s;
initialize(&s);
for (int i = 0; expression[i] != '