目录
顺序栈
基本概念
顺序栈是一种基于数组实现的栈结构,利用连续的内存空间存储数据,遵循"后进先出"(LIFO)原则。栈顶指针(通常为top)动态指向当前栈顶元素的位置,支持压栈(push)和弹栈(pop)操作。
核心特性
- 存储结构:通过数组实现,需预先分配固定大小的内存空间。
- 栈顶指针 :初始值为
-1(空栈),插入元素时top递增,删除时递减。 - 时间复杂度:压栈和弹栈操作的时间复杂度均为O(1)。
代码实现
cpp
#include <stdio.h>
// 考研标准:栈的最大容量(可根据题目修改)
#define MaxSize 50
// 顺序栈结构体定义(考研必考写法)
typedef struct {
// 静态数组存放栈元素
int data[MaxSize];
// 栈顶指针:指向栈顶元素(考研最常用定义方式)
int top;
} SqStack;
// 1. 初始化栈(核心)
void InitStack(SqStack *S) {
// 栈顶指针置为-1,表示空栈
S->top = -1;
}
// 2. 判断栈是否为空
int StackEmpty(SqStack S) {
// 栈顶为-1 → 空栈,返回1;否则返回0
return S.top == -1;
}
// 3. 判断栈是否为满
int StackFull(SqStack S) {
// 栈顶指向最后一个元素 → 栈满,返回1
return S.top == MaxSize - 1;
}
// 4. 入栈操作(进栈)
int Push(SqStack *S, int e) {
// 栈满,入栈失败
if (StackFull(*S))
return 0;
// 栈顶指针先+1,再赋值
S->data[++S->top] = e;
// 入栈成功
return 1;
}
// 5. 出栈操作(删除栈顶元素,用e返回值)
int Pop(SqStack *S, int *e) {
// 栈空,出栈失败
if (StackEmpty(*S))
return 0;
// 先取栈顶元素,栈顶指针再-1
*e = S->data[S->top--];
// 出栈成功
return 1;
}
// 6. 取栈顶元素(不删除)
int GetTop(SqStack S, int *e) {
// 栈空,取元素失败
if (StackEmpty(S))
return 0;
*e = S.data[S.top];
// 取元素成功
return 1;
}
// 7. 销毁栈(静态栈无需free,仅重置指针即可)
void DestroyStack(SqStack *S) {
S->top = -1;
}
// 主函数:测试栈的所有操作(考研答题可写可不写,建议写上)
int main() {
// 定义一个栈
SqStack S;
// 初始化栈
InitStack(&S);
// 测试入栈
Push(&S, 10);
Push(&S, 20);
Push(&S, 30);
printf("入栈元素:10,20,30\n");
// 测试取栈顶
int topElem;
GetTop(S, &topElem);
printf("当前栈顶元素:%d\n", topElem);
// 测试出栈
int e;
Pop(&S, &e);
printf("出栈元素:%d\n", e);
GetTop(S, &topElem);
printf("出栈后栈顶元素:%d\n", topElem);
// 测试判空
if (StackEmpty(S))
printf("栈为空\n");
else
printf("栈不为空\n");
// 销毁栈
DestroyStack(&S);
return 0;
}
共享栈
基本概念
共享栈是一种利用同一块内存空间实现两个栈的数据结构。两个栈的栈底分别位于共享内存空间的两端,栈顶向中间生长。当两个栈顶相遇时,表示栈空间已满。
核心特性
空间高效利用
共享栈通过让两个栈共享同一块内存区域,避免了单独分配两块内存可能导致的浪费。尤其当两个栈的实际使用空间动态变化时,能更灵活地利用内存。
双向生长
两个栈的栈顶分别从数组的两端向中间延伸。栈A从下标0开始向高位生长,栈B从下标n-1开始向低位生长。
溢出条件
共享栈的溢出条件为两个栈顶相遇(即top1 + 1 >= top2)。此时无论向哪个栈压入元素都会导致栈满。
代码实现
cpp
#include <stdio.h>
#include <stdlib.h>
// 共享栈最大容量(可根据需求修改)
#define MaxSize 10
// 共享栈结构体定义(408标准写法)
typedef struct {
// 共享存储空间
int data[MaxSize];
// 栈1栈顶指针
int top1;
// 栈2栈顶指针
int top2;
} ShareStack;
//==================== 基本操作 ====================//
// 1. 初始化共享栈
void InitStack(ShareStack *S) {
// 栈1空栈状态
S->top1 = -1;
// 栈2空栈状态
S->top2 = MaxSize;
}
// 2. 判断共享栈是否为空
// flag=1 判栈1空;flag=2 判栈2空
int StackEmpty(ShareStack S, int flag) {
if (flag == 1) {
// 栈1空返回1
return S.top1 == -1;
} else if (flag == 2) {
// 栈2空返回1
return S.top2 == MaxSize;
}
// 非法标记
return -1;
}
// 3. 判断共享栈是否满
int StackFull(ShareStack S) {
// 标准判满条件
return S.top1 + 1 == S.top2;
}
// 4. 入栈操作
// flag=1 入栈1;flag=2 入栈2
int Push(ShareStack *S, int flag, int e) {
// 栈满,入栈失败
if (StackFull(*S)) {
printf("共享栈已满,入栈失败!\n");
return 0;
}
if (flag == 1) {
// 栈1:先移动指针,再入栈
S->data[++S->top1] = e;
} else if (flag == 2) {
// 栈2:先移动指针,再入栈
S->data[--S->top2] = e;
} else {
printf("栈编号错误!\n");
return 0;
}
return 1;
}
// 5. 出栈操作
// flag=1 出栈1;flag=2 出栈2,e保存出栈元素
int Pop(ShareStack *S, int flag, int *e) {
if (flag == 1) {
// 栈1空
if (S->top1 == -1) {
printf("栈1为空,出栈失败!\n");
return 0;
}
// 先出栈,再移动指针
*e = S->data[S->top1--];
} else if (flag == 2) {
// 栈2空
if (S->top2 == MaxSize) {
printf("栈2为空,出栈失败!\n");
return 0;
}
// 先出栈,再移动指针
*e = S->data[S->top2++];
} else {
printf("栈编号错误!\n");
return 0;
}
return 1;
}
// 6. 获取栈顶元素(不删除)
int GetTop(ShareStack S, int flag, int *e) {
if (flag == 1) {
if (S.top1 == -1) return 0;
*e = S.data[S.top1];
} else if (flag == 2) {
if (S.top2 == MaxSize) return 0;
*e = S.data[S.top2];
} else return 0;
return 1;
}
//==================== 测试主函数 ====================//
int main() {
ShareStack S;
// 初始化
InitStack(&S);
int e;
// 栈1入栈
Push(&S, 1, 10);
Push(&S, 1, 20);
Push(&S, 1, 30);
// 栈2入栈
Push(&S, 2, 100);
Push(&S, 2, 200);
// 栈1出栈
Pop(&S, 1, &e);
printf("栈1出栈元素:%d\n", e);
// 栈2出栈
Pop(&S, 2, &e);
printf("栈2出栈元素:%d\n", e);
// 获取栈顶
GetTop(S, 1, &e);
printf("栈1栈顶元素:%d\n", e);
GetTop(S, 2, &e);
printf("栈2栈顶元素:%d\n", e);
return 0;
}
链栈
基本概念
链栈是一种基于链表实现的栈结构,采用动态内存分配方式存储数据。与顺序栈不同,链栈无需预先定义固定容量,通过节点间的指针链接实现元素的入栈和出栈操作。链栈的栈顶通常为链表的头节点,操作均在链表头部完成,保证时间复杂度为 O(1)。
链栈的核心特性
动态扩展性
链栈的存储空间随需求动态分配,无需担心栈满问题(除非内存耗尽)。
高效操作
入栈和出栈仅需修改头节点指针,无需移动其他元素,操作效率稳定。
内存灵活性
每个节点独立分配内存,可分散存储,但需额外空间存储指针。
实现关键
- 栈顶指针指向链表头节点。
- 空栈时,栈顶指针为
NULL。 - 节点结构包含数据域和指向下一节点的指针域。
代码实现
带头结点的链栈
cpp
#include <stdio.h>
#include <stdlib.h>
// 链栈结点类型定义
typedef struct StackNode {
int data;
struct StackNode *next;
} StackNode, *StackPtr;
// 链栈(仅需栈顶指针,指向头结点)
typedef struct {
StackPtr top; // 栈顶指针,指向头结点
} LinkStack;
// 1. 初始化带头结点的链栈
int InitStack(LinkStack *S) {
// 创建头结点
S->top = (StackPtr)malloc(sizeof(StackNode));
if (S->top == NULL) return 0; // 内存分配失败
S->top->next = NULL; // 头结点后继为空
return 1;
}
// 2. 判断栈空
int StackEmpty(LinkStack S) {
// 头结点后无元素即为空
return S.top->next == NULL;
}
// 3. 入栈(头插法)
int Push(LinkStack *S, int e) {
StackPtr p = (StackPtr)malloc(sizeof(StackNode));
if (p == NULL) return 0;
p->data = e;
p->next = S->top->next; // 新结点指向原第一个元素
S->top->next = p; // 头结点指向新结点
return 1;
}
// 4. 出栈
int Pop(LinkStack *S, int *e) {
if (StackEmpty(*S)) return 0; // 栈空
StackPtr p = S->top->next; // p指向待删除结点
*e = p->data;
S->top->next = p->next; // 摘链
free(p);
return 1;
}
// 5. 取栈顶元素(不删除)
int GetTop(LinkStack S, int *e) {
if (StackEmpty(S)) return 0;
*e = S.top->next->data;
return 1;
}
// 6. 销毁栈
int DestroyStack(LinkStack *S) {
StackPtr p, q;
p = S->top;
while (p != NULL) {
q = p->next;
free(p);
p = q;
}
S->top = NULL;
return 1;
}
// 测试主函数
int main() {
LinkStack S;
int e;
InitStack(&S);
Push(&S, 10);
Push(&S, 20);
Push(&S, 30);
GetTop(S, &e);
printf("栈顶元素:%d\n", e);
Pop(&S, &e);
printf("出栈元素:%d\n", e);
GetTop(S, &e);
printf("新栈顶:%d\n", e);
printf("栈是否为空:%s\n", StackEmpty(S) ? "是" : "否");
DestroyStack(&S);
return 0;
}
不带头结点的链栈
cpp
#include <stdio.h>
#include <stdlib.h>
typedef struct StackNode {
int data;
struct StackNode *next;
} StackNode, *StackPtr;
typedef struct {
StackPtr top;
} LinkStack;
// 初始化
void InitStack(LinkStack *S) {
S->top = NULL;
}
// 判空
int StackEmpty(LinkStack S) {
return S.top == NULL;
}
// 入栈
int Push(LinkStack *S, int e) {
StackPtr p = (StackPtr)malloc(sizeof(StackNode));
if (!p) return 0;
p->data = e;
p->next = S->top;
S->top = p;
return 1;
}
// 出栈
int Pop(LinkStack *S, int *e) {
if (StackEmpty(*S)) return 0;
StackPtr p = S->top;
*e = p->data;
S->top = p->next;
free(p);
return 1;
}
// 取栈顶
int GetTop(LinkStack S, int *e) {
if (StackEmpty(S)) return 0;
*e = S.top->data;
return 1;
}
// 销毁栈
void DestroyStack(LinkStack *S) {
StackPtr p, q;
p = S->top;
while (p != NULL) {
q = p->next;
free(p);
p = q;
}
S->top = NULL; // 销毁后置空,避免野指针
}
// 测试
int main() {
LinkStack S;
int e;
InitStack(&S);
Push(&S, 10);
Push(&S, 20);
Push(&S, 30);
GetTop(S, &e);
printf("栈顶:%d\n", e);
Pop(&S, &e);
printf("出栈:%d\n", e);
DestroyStack(&S); // 用完销毁
return 0;
}