堆栈的基础概念
堆栈Stack
是一种后进先出Last In First Ou
的数据结构,一种特殊的线性表,只允许在表的一端进行插入和删除操作,这一端被称为栈顶(top),另一端则称为栈底(bottom),最后插入的元素将最先被删除。
堆栈的操作与汉诺塔的递归操作有相似的地方,在递归解决汉诺塔问题的过程中,每次递归调用都会将当前问题的状态(即哪些圆盘已经被移动,哪些圆盘还在起始柱上)保存在调用栈中。当递归返回时,这些状态会按照后进先出的顺序被恢复,这与堆栈的后进先出特性有相似之处。而不同的是堆栈的操作对象是数据元素,这些元素可以是任何类型的数据。
C语言中堆栈的操作流程
在C语言中,关于堆栈的操作主要依赖于数组或链表来实现,其操作流程如下:
-
初始化堆栈(Init Stack):
- 为堆栈分配空间,设置堆栈的最大容量,并将栈顶指针设置为-1或0,表示堆栈为空。
-
判断堆栈是否为空(Is Empty):
- 检查栈顶指针是否为-1或0。
-
判断堆栈是否已满(Is Full):
- 检查栈顶指针是否等于堆栈的最大容量减1。
-
入栈(Push):
- 在堆栈未满的情况下,将栈顶指针加1,然后在栈顶指针所指的位置放入新元素。
-
出栈(Pop):
- 在堆栈非空的情况下,取出栈顶元素,然后将栈顶指针减1。
-
获取栈顶元素(Peek/Top):
- 返回栈顶元素的值,但不改变堆栈的内容。
-
销毁堆栈(Destroy Stack):
- 释放堆栈所占用的空间,通常是在程序结束时进行。
以下是一个简单的堆栈操作的例子,将数组 [1,2,3,4,5,6,7,8,9,10]
转换为 [10,9,8,7,6,5,4,3,2,1]
:
#include <stdio.h>
#include <stdlib.h>
#define SIZE 10 // 定义栈的大小
// 定义一个栈结构
typedef struct
{
int items[SIZE]; // 栈中用于存储元素的数组
int top; // 指向栈顶元素的索引
} Stack;
void initializeStack(Stack *stack); // 初始化栈,设置栈顶索引为-1,表示栈为空
int isEmpty(Stack *stack) ; // 判断栈是否为空,返回1(或true)表示栈为空,返回0(或false)表示栈不为空
int isFull(Stack *stack); // 判断栈是否已满,返回1(或true)表示栈已满,返回0(或false)表示栈未满
void push(Stack *stack, int value); // 将一个整数值压入栈中
int pop(Stack *stack); // 从栈中弹出一个元素并返回
int main()
{
int array[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int reversedArray[SIZE];
Stack stack;
initializeStack(&stack);
// 将数组元素依次入栈
for (int i = 0; i < SIZE; i++)
{
push(&stack, array[i]);
}
// 从栈中依次弹出元素并存入新数组
for (int i = 0; i < SIZE; i++)
{
reversedArray[i] = pop(&stack);
}
printf("反转后的数组: ");
for (int i = 0; i < SIZE; i++)
{
printf("%d ", reversedArray[i]);
}
printf("\n");
return 0;
}
// 初始化栈
void initializeStack(Stack *stack)
{
stack->top = -1;
}
// 判断栈是否为空
int isEmpty(Stack *stack)
{
return stack->top == -1;
}
// 判断栈是否已满
int isFull(Stack *stack)
{
return stack->top == SIZE - 1;
}
// 入栈
void push(Stack *stack, int value)
{
if (!isFull(stack))
{
stack->items[++stack->top] = value;
}
else
{
printf("Stack is full!\n");
}
}
// 出栈
int pop(Stack *stack)
{
if (!isEmpty(stack))
{
return stack->items[stack->top--];
}
else
{
printf("Stack is empty!\n");
return -1;
}
}
堆栈的存储方式
顺序存储(顺序栈):利用一组地址连续的存储单元依次存放自栈底到栈顶的元素,同时使用指针top指示栈顶元素在顺序栈中的位置,刚才的例子既是顺序式操作。
链式存储(链式栈):利用单链表的方式来实现堆栈。栈中元素按照插入顺序依次插入到链表的第一个节点之前,并使用栈顶指针top指示栈顶元素。
接下来使用链式存储来进行堆栈操作,完成逆波兰表达式(Reverse Polish Notation, RPN)
,逆波兰表达式(Reverse Polish Notation, RPN)是一种后缀表达式,其中操作符跟在操作数之后。为了求值逆波兰表达式,可以使用链表堆栈来存储操作数,并在遇到操作符时从堆栈中弹出相应数量的操作数进行计算,然后将结果再次压入堆栈,最后堆栈中剩下的唯一元素就是表达式的计算结果。
比如:
常用的中辍表达式为:(3 + 4) * 2 - 5
逆波兰表达式为:3 4 + 2 * 5 -
最终计算的值为:9
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// 定义链式堆栈的节点结构
typedef struct StackNode
{
int data;
struct StackNode* next;
} StackNode;
// 定义堆栈结构,包含指向栈顶的指针
typedef struct Stack
{
StackNode* top;
} Stack;
// 初始化堆栈
void initializeStack(Stack* stack)
{
stack->top = NULL;
}
// 检查堆栈是否为空
int isEmpty(Stack* stack)
{
return stack->top == NULL;
}
// 入栈操作
void push(Stack* stack, int value)
{
StackNode* newNode = (StackNode*)malloc(sizeof(StackNode));
if (!newNode)
{
perror("内存分配失败");
exit(EXIT_FAILURE);
}
newNode->data = value;
newNode->next = stack->top;
stack->top = newNode;
}
// 出栈操作
int pop(Stack* stack)
{
if (isEmpty(stack))
{
perror("栈是空的");
exit(EXIT_FAILURE);
}
StackNode* tempNode = stack->top;
int poppedValue = tempNode->data;
stack->top = tempNode->next;
free(tempNode);
return poppedValue;
}
// 获取操作符的优先级
int precedence(char op)
{
switch (op)
{
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return 0;
}
}
// 执行基本的算术运算
int applyOp(int a, int b, char op)
{
switch (op)
{
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
default: perror("无效运算符"); exit(EXIT_FAILURE);
}
}
// 计算逆波兰表达式
int evaluateRPN(char** tokens, int numTokens)
{
Stack stack;
initializeStack(&stack);
for (int i = 0; i < numTokens; i++)
{
char* token = tokens[i];
// 如果是数字,则入栈
if (isdigit(token[0])) {
push(&stack, atoi(token));
}
// 如果是操作符,则从栈中弹出两个操作数,计算结果后再入栈
else if (strchr("+-*/", token[0]))
{
int b = pop(&stack);
int a = pop(&stack);
int result = applyOp(a, b, token[0]);
push(&stack, result);
}
// 不是数字或操作符,则输入格式有误
else
{
perror("输入格式错误");
exit(EXIT_FAILURE);
}
}
// 最后栈中应该只剩下一个元素,即结果
if (!isEmpty(&stack))
{
int result = pop(&stack);
// 确保栈已经为空
if (isEmpty(&stack))
{
return result;
}
else
{
perror("求值后栈不为空");
exit(EXIT_FAILURE);
}
}
else
{
perror("弹出结果前栈为空");
exit(EXIT_FAILURE);
}
}
int main()
{
// 逆波兰表达式:3 4 + 2 * 5 -
char* tokens[] = {"3", "4", "+", "2", "*", "5", "-"};
int numTokens = sizeof(tokens) / sizeof(tokens[0]);
int result = evaluateRPN(tokens, numTokens);
printf("计算结果= %d\n", result);
return 0;
}