目录
栈是一种数据结构,像一个倒立的容器。想象一下,把物品一个个放进去,但每次只能从最上面取出物品。也就是说,最后放进去的物品会最先被取出来,这种顺序叫做后进先出(LIFO)。在栈中,有两个主要操作:
1.栈的基本操作
**入栈(Push):**将元素压入栈顶。
出栈(Pop):从栈顶移除元素。
查看栈顶(Peek 或 Top):获取栈顶元素,但不移除它。
检查栈是否为空(IsEmpty):判断栈中是否还有元素
2.栈的实现
栈可以通过以下两种方式实现:
2.1基于数组
用固定大小的数组,直接通过索引操作栈顶元素,但需要手动管理栈的大小。
objectivec
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100 // 定义栈的最大容量
// 栈结构体
typedef struct {
int data[MAX_SIZE]; // 存储栈元素的数组
int top; // 栈顶指针,指向栈顶元素
} Stack;
// 初始化栈
void initStack(Stack *s) {
s->top = -1; // 栈为空时,栈顶指针为-1
}
// 判断栈是否为空
int isEmpty(Stack *s) {
return s->top == -1;
}
// 判断栈是否满
int isFull(Stack *s) {
return s->top == MAX_SIZE - 1;
}
// 入栈操作
void push(Stack *s, int value) {
if (isFull(s)) {
printf("Stack Overflow!\n");
return;
}
s->data[++(s->top)] = value; // 将值放入栈顶并更新栈顶指针
}
// 出栈操作
int pop(Stack *s) {
if (isEmpty(s)) {
printf("Stack Underflow!\n");
return -1; // 返回一个表示错误的值
}
return s->data[(s->top)--]; // 返回栈顶元素并更新栈顶指针
}
// 查看栈顶元素
int peek(Stack *s) {
if (isEmpty(s)) {
printf("Stack is empty!\n");
return -1; // 返回一个表示错误的值
}
return s->data[s->top];
}
// 打印栈的内容
void printStack(Stack *s) {
if (isEmpty(s)) {
printf("Stack is empty!\n");
return;
}
for (int i = 0; i <= s->top; i++) {
printf("%d ", s->data[i]);
}
printf("\n");
}
int main() {
Stack s;
initStack(&s); // 初始化栈
push(&s, 10);
push(&s, 20);
push(&s, 30);
printf("Stack after pushing 10, 20, 30: ");
printStack(&s);
printf("Popped element: %d\n", pop(&s));
printf("Stack after pop: ");
printStack(&s);
printf("Top element: %d\n", peek(&s));
return 0;
}
注意事项:
++(s->top)
确保栈顶指针在插入之前递增。(s->top)++
先插入元素,再递增栈顶指针。
栈为空的条件不再是 top == -1
,而是 top == 0
。如果栈初始化为 0,那么后续遍历的范围也应该变化。
2.2基于链表
动态分配内存,插入和删除操作更灵活,基于链表实现栈的方式相比基于数组的实现,具有更好的灵活性,因为链表不需要事先定义大小,内存分配是动态的。链表实现的栈可以随时扩展或收缩,插入和删除元素的时间复杂度均为 O(1),非常高效。
objectivec
#include <stdio.h>
#include <stdlib.h>
// 栈的节点结构体
typedef struct Node {
int data; // 存储栈的值
struct Node *next; // 指向下一个节点
} Node;
// 栈的结构体
typedef struct {
Node *top; // 栈顶指针,指向栈顶元素
} Stack;
// 初始化栈
void initStack(Stack *s) {
s->top = NULL; // 初始化时,栈为空
}
// 判断栈是否为空
int isEmpty(Stack *s) {
return s->top == NULL; // 栈顶指针为NULL时栈为空
}
// 入栈操作
void push(Stack *s, int value) {
// 创建一个新的节点
Node *newNode = (Node *)malloc(sizeof(Node));
if (newNode == NULL) {
printf("Memory allocation failed!\n");
return;
}
newNode->data = value;
newNode->next = s->top; // 新节点指向当前栈顶
s->top = newNode; // 更新栈顶指针
}
// 出栈操作
int pop(Stack *s) {
if (isEmpty(s)) {
printf("Stack Underflow!\n");
return -1; // 返回一个表示错误的值
}
Node *temp = s->top; // 保存栈顶节点
int value = temp->data; // 获取栈顶值
s->top = s->top->next; // 更新栈顶指针
free(temp); // 释放栈顶节点的内存
return value; // 返回栈顶元素
}
// 查看栈顶元素
int peek(Stack *s) {
if (isEmpty(s)) {
printf("Stack is empty!\n");
return -1; // 返回一个表示错误的值
}
return s->top->data; // 返回栈顶元素
}
// 打印栈的内容
void printStack(Stack *s) {
if (isEmpty(s)) {
printf("Stack is empty!\n");
return;
}
Node *current = s->top;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
// 主函数
int main() {
Stack s;
initStack(&s); // 初始化栈
push(&s, 10);
push(&s, 20);
push(&s, 30);
printf("Stack after pushing 10, 20, 30: ");
printStack(&s);
printf("Popped element: %d\n", pop(&s));
printf("Stack after pop: ");
printStack(&s);
printf("Top element: %d\n", peek(&s));
return 0;
}
节点结构体 :Node
结构体包含一个 data
字段用于存储栈的元素,以及一个 next
指针指向下一个节点。链表结构就是通过这些节点组成的。
栈结构体 :Stack
结构体只有一个成员 top
,它是一个指向栈顶节点的指针。
初始化栈 :initStack
函数将栈顶指针 top
初始化为 NULL
,表示栈为空。
判断栈是否为空 :isEmpty
函数检查栈顶指针是否为 NULL
来判断栈是否为空。
入栈操作 :push
函数首先为新元素分配内存,然后将它插入到链表的头部。新节点的 next
指向当前的栈顶元素,然后更新栈顶指针。
出栈操作 :pop
函数删除栈顶节点,更新栈顶指针,并返回被删除节点的数据。释放栈顶节点的内存。
查看栈顶元素 :peek
函数返回栈顶元素的值,但不删除该元素。
打印栈内容 :printStack
函数遍历链表并打印栈中的所有元素。