- 第 110 篇 -
Date: 2025 - 07 - 16
Author: 郑龙浩(仟墨)
文章目录
- 栈 (Stack)
-
- [1 顺序栈介绍](#1 顺序栈介绍)
-
- [1.1 定义](#1.1 定义)
- [1.2 规则](#1.2 规则)
- 1.3基本操作
- [2 顺序栈 - top指向栈顶元素](#2 顺序栈 - top指向栈顶元素)
-
- [test.c 文件](#test.c 文件)
- [SqStack.c 文件](#SqStack.c 文件)
- [SqStack.h 文件](#SqStack.h 文件)
- [3 顺序栈 - top指向栈顶元素后面](#3 顺序栈 - top指向栈顶元素后面)
-
- [test.c 文件](#test.c 文件)
- [SqStack.c 文件](#SqStack.c 文件)
- [SqStack.h 文件](#SqStack.h 文件)
- [5 顺序栈 top指向不同的区别](#5 顺序栈 top指向不同的区别)
- [4 共享栈(别名: 双向栈、双端栈)](#4 共享栈(别名: 双向栈、双端栈))
- [5 链栈介绍](#5 链栈介绍)
-
- [5.1 基本介绍](#5.1 基本介绍)
- [5.2 基本操作](#5.2 基本操作)
- [6 链栈_无头结点(推荐,因为教材上是这个版本)](#6 链栈_无头结点(推荐,因为教材上是这个版本))
- [7 链栈_带头结点](#7 链栈_带头结点)
Author: 郑龙浩
Date: 2025.07.16
栈 (Stack)
1 顺序栈介绍
1.1 定义
栈也是一种线性表,是只允许在一端进行插入或删除操作的线性表
和其他的线性表结构是相同的,只是只能在一端操作
1.2 规则
只允许在一端口进行插入和删除的操作 --> 后进先出 LIFO(Last In First Out)
栈就像一摞盘子
**重要术语: ** 栈顶、栈底、空栈
- 栈顶(栈顶元素): 允许插入和删除的一端(最顶端盘子)
- 栈底(栈底元素): 不允许插入行和删除的一端(最低端的盘子)
- 空栈: 没有任何元素的栈(没有盘子)
1.3基本操作
- 创建、销毁:
InisStack(SqStack* s)
、DestroyStack(SqStack* s)
- 进栈、出栈:
Push(SqStack* s, ElemType x)
、Pop(SqStack* s, ElemType x)
(增、删) - 读取栈顶元素:
GetTop(SqStack* s, x)
(查) - 判断是否为空、满:
IsStackEmpty(SqStack* s)
、IsStackFull(SqStack* s)
2 顺序栈 - top指向栈顶元素
top 的范围是
-1 到 MaxSize - 1
- 空栈的时候 top 指向 -1
- 只有一个元素时,top 指向 0
- 满栈的时候 top 指向栈顶元素
test.c 文件
c
#include "SqStack.h"
#include <stdio.h>
void Test1() {
SqStack S;
ElemType x;
// 1. 初始化测试
InitStack(&S);
printf("初始化后栈是否为空?%s\n", IsStackEmpty(&S) ? "是" : "否");
// 2. 入栈测试
printf("\n入栈顺序:");
for (int i = 1; i <= MaxSize; i++) {
if (StackPush(&S, i * 10)) {
printf("%d ", i * 10);
}
else {
printf("\n第%d次入栈失败(栈满)\n", i);
}
}
printf("\n栈是否已满?%s\n", IsStackFull(&S) ? "是" : "否");
// 3. 栈顶读取测试
if (StackGetTop(&S, &x)) {
printf("\n当前栈顶元素:%d\n", x);
}
// 边界测试:满栈入栈
if (!StackPush(&S, 110)) {
printf("\n已经满栈,入栈失败\n");
}
// 4. 出栈测试
printf("\n出栈顺序:");
while (!IsStackEmpty(&S)) {
if (StackPop(&S, &x)) {
printf("%d ", x);
}
}
printf("\n出栈后栈是否为空?%s\n", IsStackEmpty(&S) ? "是" : "否");
// 5. 边界测试:空栈出栈
if (!StackPop(&S, &x)) {
printf("\n现在是空栈,出栈操作失败\n");
}
// 6. 销毁栈测试
DestroyStack(&S);
printf("\n销毁栈后top值:%d\n", S.top);
}
int main() {
Test1();
return 0;
}
运行结果
初始化后栈是否为空?是
入栈顺序:10 20 30 40 50 60 70 80 90 100
栈是否已满?是
当前栈顶元素:100
已经满栈,入栈失败
出栈顺序:100 90 80 70 60 50 40 30 20 10
出栈后栈是否为空?是
现在是空栈,出栈操作失败
销毁栈后top值:-1
SqStack.c 文件
c
#include "SqStack.h"
#include "stdio.h"
// 初始化
void InitStack(SqStack* S) {
S->top = -1; // 默认空栈 top 指向-1
}
// 销毁
void DestroyStack(SqStack* S) {
S->top = -1;
}
// 进栈
int StackPush(SqStack* S, ElemType x) {
// x 是要入栈的元素
if (S->top == MaxSize - 1)
return 0; // 如果满栈,不能入栈,返回0
S->data[++S->top] = x;
return 1; // 返回1,表示进栈成功
}
// 出栈
int StackPop(SqStack* S, ElemType *x) {
if (S->top == -1)
return 0; // 如果空栈,不能出栈,返回0
*x = S->data[S->top--]; // x 带回栈顶元素
return 1;
}
// 读取栈顶
int StackGetTop(SqStack* S, ElemType *x) {
if (S->top == -1)
return 0; // 如果空栈,则读取不出来元素,返回0
*x = S->data[S->top];
return 1; // 返回1,表示进栈成功
}
// 判断栈空
int IsStackEmpty(SqStack* S) {
if (S->top == -1) // 空1,非空0
return 1;
else
return 0;
}
// 判断栈满
int IsStackFull(SqStack* S) {
if (S->top == MaxSize - 1)
return 1;
else
return 0;
}
SqStack.h 文件
c
#pragma once
#define MaxSize 10
typedef int ElemType;
typedef struct {
ElemType data[MaxSize]; // 存储元素
int top; // 指向栈顶元素
}SqStack;
// 初始化
void InitStack(SqStack* S);
// 销毁
void DestroyStack(SqStack* S);
// 进栈
int StackPush(SqStack* S, ElemType x);
// 出栈
int StackPop(SqStack* S, ElemType *x);
// 读取栈顶
int StackGetTop(SqStack* S, ElemType *x);
// 判断栈空
int IsStackEmpty(SqStack* S);
// 判断栈满
int IsStackFull(SqStack* S);
3 顺序栈 - top指向栈顶元素后面
top 的范围是
0 到 MaxSize
- 空栈的时候 top 指向 0
- 只有一个元素时,top 指向 1
- 满栈的时候 top 指向栈顶元素后边
test.c 文件
c
#include "SqStack.h"
#include <stdio.h>
void Test1() {
SqStack S;
ElemType x;
// 1. 初始化测试
InitStack(&S);
printf("初始化后栈是否为空?%s\n", IsStackEmpty(&S) ? "是" : "否");
// 2. 入栈测试
printf("\n入栈顺序:");
for (int i = 1; i <= MaxSize; i++) {
if (StackPush(&S, i * 10)) {
printf("%d ", i * 10);
}
else {
printf("\n第%d次入栈失败(栈满)\n", i);
}
}
printf("\n栈是否已满?%s\n", IsStackFull(&S) ? "是" : "否");
// 3. 栈顶读取测试
if (StackGetTop(&S, &x)) {
printf("\n当前栈顶元素:%d\n", x);
}
// 边界测试:满栈入栈
if (!StackPush(&S, 110)) {
printf("\n已经满栈,入栈失败\n");
}
// 4. 出栈测试
printf("\n出栈顺序:");
while (!IsStackEmpty(&S)) {
if (StackPop(&S, &x)) {
printf("%d ", x);
}
}
printf("\n出栈后栈是否为空?%s\n", IsStackEmpty(&S) ? "是" : "否");
// 5. 边界测试:空栈出栈
if (!StackPop(&S, &x)) {
printf("\n现在是空栈,出栈操作失败\n");
}
// 6. 销毁栈测试
DestroyStack(&S);
printf("\n销毁栈后top值:%d\n", S.top);
}
int main() {
Test1();
return 0;
}
运行结果
初始化后栈是否为空?是
入栈顺序:10 20 30 40 50 60 70 80 90 100
栈是否已满?是
当前栈顶元素:100
已经满栈,入栈失败
出栈顺序:100 90 80 70 60 50 40 30 20 10
出栈后栈是否为空?是
现在是空栈,出栈操作失败
销毁栈后top值:0
SqStack.c 文件
c
#include "SqStack.h"
#include "stdio.h"
// 初始化
void InitStack(SqStack* S) {
S->top = 0; // 默认空栈 top 指向0
}
// 销毁
void DestroyStack(SqStack* S) {
S->top = 0;
}
// 进栈
int StackPush(SqStack* S, ElemType x) {
// x 是要入栈的元素
if (S->top == MaxSize)
return 0; // 如果满栈,不能入栈,返回0
S->data[S->top++] = x;
return 1; // 返回1,表示进栈成功
}
// 出栈
int StackPop(SqStack* S, ElemType* x) {
if (S->top == 0)
return 0; // 如果空栈,不能出栈,返回0
*x = S->data[--S->top]; // x 带回栈顶元素
return 1;
}
// 读取栈顶
int StackGetTop(SqStack* S, ElemType* x) {
if (S->top == 0)
return 0; // 如果空栈,则读取不出来元素,返回0
*x = S->data[S->top - 1];
return 1; // 返回1,表示进栈成功
}
// 判断栈空
int IsStackEmpty(SqStack* S) {
if (S->top == 0) // 空1,非空0
return 1;
else
return 0;
}
// 判断栈满
int IsStackFull(SqStack* S) {
if (S->top == MaxSize)
return 1;
else
return 0;
}
SqStack.h 文件
c
#pragma once
#define MaxSize 10
typedef int ElemType;
typedef struct {
ElemType data[MaxSize]; // 存储元素
int top; // 指向栈顶元素
}SqStack;
// 初始化
void InitStack(SqStack* S);
// 销毁
void DestroyStack(SqStack* S);
// 进栈
int StackPush(SqStack* S, ElemType x);
// 出栈
int StackPop(SqStack* S, ElemType *x);
// 读取栈顶
int StackGetTop(SqStack* S, ElemType *x);
// 判断栈空
int IsStackEmpty(SqStack* S);
// 判断栈满
int IsStackFull(SqStack* S);
5 顺序栈 top指向不同的区别
操作 | top 指向栈顶元素和后边 | top 指向栈顶元素 |
---|---|---|
初始化 | S->top = 0 |
S->top = -1 |
栈空条件 | top == 0 |
top == -1 |
栈满条件 | top == MaxSize |
top == MaxSize - 1 |
进栈操作 | S->data[S->top++] = x |
S->data[++S->top] = x |
出栈操作 | *x = S->data[--S->top] |
*x = S->data[S->top--] |
栈顶元素位置 | data[top - 1] |
data[top] |
首个元素位置 | data[0] |
data[0] |
top指向? | top指向下一个入栈的元素位置(栈顶元素后边) | top直接指向当前栈顶元素 |
4 共享栈(别名: 双向栈、双端栈)
基本介绍
是一种将两个栈共享同一块连续内存空间的数据结构。
- 两个栈共享同一个数组空间
- 栈0:一个栈从数组的起始位置(通常称为"下端"或"栈0")开始生长,top0指向这个栈的栈顶元素
- 栈1:一个栈从数组的末尾位置(通常称为"上端"或"栈1")开始生长,top1指向这个栈的栈顶元素
- 栈0:向数组末尾方向(向MaxSize-1)增长(下标递增);栈1:向数组起始方向(向0)增长(下标递减)
- 栈满:当两个栈的栈顶相遇(top0 > top1)时,表示栈空间已满
test.c
c
#include "stdio.h"
#include "ShStack.h"
int main() {
ShStack S;
ElemType x;
// 初始化测试
InitStack(&S);
printf("初始化测试: %s\n", IsStackEmpty(&S) ? "正确" : "错误");
// 栈0入栈测试
printf("栈0入栈: %s\n", StackPush(&S, 0, 10) ? "正确" : "错误");
printf("栈0非空: %s\n", !IsStack0Empty(&S) ? "正确" : "错误");
// 栈1入栈测试
printf("栈1入栈: %s\n", StackPush(&S, 1, 20) ? "正确" : "错误");
printf("栈1非空: %s\n", !IsStack1Empty(&S) ? "正确" : "错误");
// 栈顶读取测试
printf("栈0栈顶: %s\n", StackGetTop(&S, 0, &x) && x == 10 ? "正确" : "错误");
printf("栈1栈顶: %s\n", StackGetTop(&S, 1, &x) && x == 20 ? "正确" : "错误");
// 出栈测试
printf("栈0出栈: %s\n", StackPop(&S, 0, &x) && x == 10 ? "正确" : "错误");
printf("栈1出栈: %s\n", StackPop(&S, 1, &x) && x == 20 ? "正确" : "错误");
// 栈空测试
printf("栈0空栈: %s\n", IsStack0Empty(&S) ? "正确" : "错误");
printf("栈1空栈: %s\n", IsStack1Empty(&S) ? "正确" : "错误");
// 栈满测试
for (int i = 0; i < MaxSize / 2; i++) StackPush(&S, 0, i);
for (int i = 0; i < MaxSize / 2; i++) StackPush(&S, 1, i);
printf("栈满测试: %s\n", IsStackFull(&S) ? "正确" : "错误");
// 销毁测试
DestroyStack(&S);
printf("销毁测试: %s\n", IsStackEmpty(&S) ? "正确" : "错误");
return 0;
}
运行结果
初始化测试: 正确
栈0入栈: 正确
栈0非空: 正确
栈1入栈: 正确
栈1非空: 正确
栈0栈顶: 正确
栈1栈顶: 正确
栈0出栈: 正确
栈1出栈: 正确
栈0空栈: 正确
栈1空栈: 正确
栈满测试: 正确
销毁测试: 正确
ShStack.h
c
#pragma once
#define MaxSize 10
typedef int ElemType;
typedef struct {
ElemType data[MaxSize]; // 存储元素
int top0; // 栈0 的栈顶指针
int top1; // 栈1 的栈顶指针
}ShStack;
// 初始化
void InitStack(ShStack* S);
// 销毁
void DestroyStack(ShStack* S);
// 进栈
int StackPush(ShStack* S, int StackNum, ElemType x);
// 出栈
int StackPop(ShStack* S, int StackNum, ElemType* x);
// 读取栈顶
int StackGetTop(ShStack* S, int StackNum, ElemType* x);
// 判断栈空
int IsStackEmpty(ShStack* S);
// 判断栈0空
int IsStack0Empty(ShStack* S);
// 判断栈1空
int IsStack1Empty(ShStack* S);
// 判断栈满
int IsStackFull(ShStack* S);
ShStack.c
c
#include "stdio.h"
#include "ShStack.h"
// 初始化
void InitStack(ShStack* S) {
S->top0 = -1;
S->top1 = MaxSize - 1;
}
// 销毁
void DestroyStack(ShStack* S) {
S->top0 = -1;
S->top1 = MaxSize - 1;
}
// 进栈
int StackPush(ShStack* S, int StackNum, ElemType x) {
if (S->top0+1 == S->top1) // 如果满栈则无法入栈,返回0
return 0;
// 栈0 入栈
if (StackNum == 0) {
S->data[++S->top0] = x;
return 1; // 成功入栈
}
// 栈1 入栈
if (StackNum == 1) {
S->data[--S->top1] = x;
return 1; // 成功入栈
}
return 0; // 输入参数错误,也会返回0
}
// 出栈
int StackPop(ShStack* S, int StackNum, ElemType* x) {
if (StackNum == 0) {
if (S->top0 == -1) return 0; // 若栈0空栈,则需返回0
*x = S->data[S->top0--];
return 1;
}
if (StackNum == 1) {
if (S->top1 == MaxSize - 1) return 0; // 若栈1空栈,则需返回0
*x = S->data[S->top1++];
return 1;
}
return 0; // 输入参数错误,也会返回0
}
// 读取栈顶
int StackGetTop(ShStack* S, int StackNum, ElemType* x) {
if (StackNum == 0) {
if (S->top0 == -1) return 0; // 若栈0空栈,则需返回0
*x = S->data[S->top0];
return 1;
}
if (StackNum == 1) {
if (S->top1 == MaxSize - 1) return 0; // 若栈1空栈,则需返回0
*x = S->data[S->top1];
return 1;
}
return 0; // 输入参数错误,也会返回0
}
// 判断栈空
int IsStackEmpty(ShStack* S) {
if (S->top0 == -1 && S->top1 == MaxSize - 1)
return 1;
else
return 0;
}
// 判断栈0空
int IsStack0Empty(ShStack* S) {
if (S->top0 == -1) return 1;
else return 0;
}
// 判断栈1空
int IsStack1Empty(ShStack* S) {
if (S->top1 == MaxSize - 1) return 1;
else return 0;
}
// 判断栈满 (双栈为空)
int IsStackFull(ShStack* S) {
if (S->top0 + 1 == S->top1)
return 1;
else
return 0;
}
5 链栈介绍
5.1 基本介绍
- 链栈就是用链式存储实现的栈,和单链表结构相同
- 但是链栈多了约束条件:只能在一端进行操作(比如在头结点)
- 有两种版本:
- 1 单向带头结点不循环
- 2 单向无头结点不循环(推荐)
5.2 基本操作
- 初始化
void InitLStack(LinkLStack *S)
- 销毁
void DestroyLStack(LinkLStack *S)
- 清空
void ClearLStack(LinkLStack *S)
- 进栈
bool PushLStack(LinkLStack *S, ElemType value)
- 出栈
bool PopLStack(LinkLStack *S, ElemType *value)
- 获取栈顶元素
bool GetTopLStack(LinkLStack *S, ElemType *value)
- 判断栈空
bool IsEmptyLStack(LinkLStack *S)
- 获取栈的大小
int GetLStackLength(LinkLStack *S)
- 遍历打印
void PrintLStack(LinkLStack *S)
6 链栈_无头结点(推荐,因为教材上是这个版本)
test.c
c
#include "linked_stack_no_head.h"
#include <stdio.h>
void Test1() {
LinkStack S;
ElemType x;
// 1. 初始化测试
InitLStack(&S);
printf("初始化后栈是否为空?%s\n", IsEmptyLStack(&S) ? "是" : "否");
// 2. 入栈测试
printf("\n入栈顺序:");
for (int i = 1; i <= 5; i++) {
if (PushLStack(&S, i * 10)) {
printf("%d ", i * 10);
}
}
printf("\n\n实际存储:\n"); PrintLStack(&S); // 遍历链栈
printf("\n当前栈长度:%d\n", GetLengthLStack(&S));
// 3. 栈顶读取测试
if (GetTopLStack(&S, &x)) {
printf("\n当前栈顶元素:%d\n", x);
}
// 4. 出栈测试
printf("\n出栈顺序:");
while (!IsEmptyLStack(&S)) {
if (PopLStack(&S, &x)) {
printf("%d ", x);
}
}
printf("\n出栈后栈是否为空?%s\n", IsEmptyLStack(&S) ? "是" : "否");
printf("\n实际存储,如果不打印则表示正确:\n"); PrintLStack(&S); // 如果不打印则表示正确
// 5. 边界测试:空栈出栈
if (!PopLStack(&S, &x)) {
printf("\n现在是空栈,出栈操作失败\n");
}
// 6. 销毁栈测试
DestroyLStack(&S);
printf("\n销毁栈后top指针:%p\n", S.top);
// 7. 再次初始化后,栈的宽度是否为空
InitLStack(&S);
printf("\n再次初始化后,栈的宽度是否为空: %s", IsEmptyLStack(&S) ? "是" : "否");
}
int main() {
Test1();
return 0;
}
运行结果
初始化后栈是否为空?是
入栈顺序:10 20 30 40 50
实际存储:
栈顶 -> 50 -> 40 -> 30 -> 20 -> 10 -> 栈底
当前栈长度:5
当前栈顶元素:50
出栈顺序:50 40 30 20 10
出栈后栈是否为空?是
实际存储,如果不打印则表示正确:
现在是空栈,出栈操作失败
销毁栈后top指针:0000000000000000
再次初始化后,栈的宽度是否为空: 是
linked_stack_no_head.h
c
#pragma once
#include "stdio.h"
#include <stdbool.h>
typedef int ElemType;
// 结点结构
typedef struct LinkNode{
ElemType data; // 数据域
struct LinkNode* next; // 指针域
}LinkNode;
// 链栈结构
typedef struct {
LinkNode* top; // 栈顶元素
int length; // 链栈长度
}LinkStack;
// 初始化
void InitLStack(LinkStack* S);
// 销毁栈
void DestroyLStack(LinkStack* S);
// 清空
void ClearLStack(LinkStack* S);
// 进栈
bool PushLStack(LinkStack* S, ElemType x);
// 出栈
bool PopLStack(LinkStack* S, ElemType* x);
// 获取栈顶元素
bool GetTopLStack(LinkStack* S, ElemType* x);
// 判断栈空
bool IsEmptyLStack(LinkStack* S);
// 获取栈的大小
int GetLengthLStack(LinkStack* S);
// 遍历打印
void PrintLStack(LinkStack* S);
linked_stack_no_head.c
c
#include "linked_stack_no_head.h"
#include "stdio.h"
#include "stdlib.h"
#include "stdbool.h"
// 初始化
void InitLStack(LinkStack* S) {
S->top = NULL; // 空栈的情况下,top指向空
S->length = 0; // 空栈的情况下,链栈宽为0
}
// 如果没有头结点,那么销毁栈和清空栈是等同的效果
// 销毁栈
void DestroyLStack(LinkStack* S) {
ClearLStack(S);
}
// 清空
void ClearLStack(LinkStack* S) {
LinkNode* cur = S->top; // 存储第一个结点的地址
while (cur != NULL) {
LinkNode* next = cur->next;
free(cur);
cur = next;
}
S->top = NULL; // 栈顶指针置空
S->length = 0; // 链栈宽为0
}
// 进栈(无头结点)
bool PushLStack(LinkStack* S, ElemType x) {
LinkNode* NewNode = (LinkNode*)malloc(sizeof(LinkNode));
if (!NewNode) return false; // 内存分配失败
NewNode->data = x;
NewNode->next = S->top; // 新节点指向 原来的栈顶
S->top = NewNode; // 让栈顶指针一直指向新的 "第一个结点"
S->length++; // 长度++
return true;
}
// 出栈
bool PopLStack(LinkStack* S, ElemType* x) {
if (IsEmptyLStack(S)) return false; // 如果是空栈,返回false
LinkNode* OldTop = S->top; // 存储栈顶元素地址,以防栈顶元素地址丢失
*x = OldTop->data;
S->top = OldTop->next; // 将栈顶指针指向 原栈的倒数第二个结点,此时会丢失原栈顶元素的地址,
// 所以想要释放原栈顶元素,就得用提前存到OldTop的栈顶元素地址
free(OldTop);
S->length--; // 长度--
return true;
}
// 获取栈顶元素
bool GetTopLStack(LinkStack* S, ElemType* x) {
if (IsEmptyLStack(S)) return false; // 空栈,返回false
*x = S->top->data;
return true;
}
// 判断栈空
bool IsEmptyLStack(LinkStack* S) {
return S->top == NULL; // 如果栈顶指针指向NULL,则表示为栈空
}
// 获取栈的大小
int GetLengthLStack(LinkStack* S) {
return S->length;
}
// 遍历打印
void PrintLStack(LinkStack* S) {
if (IsEmptyLStack(S)) return;
LinkNode* cur = S->top; // 从第一个结点开始
printf("栈顶 -> ");
while (cur != NULL) {
printf("%d -> ", cur->data);
cur = cur->next;
}
printf("栈底\n");
}
7 链栈_带头结点
test.c
c
#include "linked_stack_with_head.h"
#include <stdio.h>
void Test1() {
LinkStack S;
ElemType x;
// 1. 初始化测试
InitLStack(&S);
printf("初始化后栈是否为空?%s\n", IsEmptyLStack(&S) ? "是" : "否");
// 2. 入栈测试
printf("\n入栈顺序:");
for (int i = 1; i <= 5; i++) {
if (PushLStack(&S, i * 10)) {
printf("%d ", i * 10);
}
}
printf("\n\n实际存储:\n"); PrintLStack(&S); // 遍历链栈
printf("\n当前栈长度:%d\n", GetLengthLStack(&S));
// 3. 栈顶读取测试
if (GetTopLStack(&S, &x)) {
printf("\n当前栈顶元素:%d\n", x);
}
// 4. 出栈测试
printf("\n出栈顺序:");
while (!IsEmptyLStack(&S)) {
if (PopLStack(&S, &x)) {
printf("%d ", x);
}
}
printf("\n出栈后栈是否为空?%s\n", IsEmptyLStack(&S) ? "是" : "否");
printf("\n实际存储,如果不打印则表示正确:\n"); PrintLStack(&S); // 如果不打印则表示正确
// 5. 边界测试:空栈出栈
if (!PopLStack(&S, &x)) {
printf("\n现在是空栈,出栈操作失败\n");
}
// 6. 销毁栈测试
DestroyLStack(&S);
printf("\n销毁栈后top指针:%p\n", S.top);
// 7. 再次初始化后,栈的宽度是否为空
InitLStack(&S);
printf("\n再次初始化后,栈的宽度是否为空: %s", IsEmptyLStack(&S) ? "是" : "否");
}
int main() {
Test1();
return 0;
}
运行结果
初始化后栈是否为空?是
入栈顺序:10 20 30 40 50
实际存储:
栈顶(头结点) -> 50 -> 40 -> 30 -> 20 -> 10 -> 栈底
当前栈长度:5
当前栈顶元素:50
出栈顺序:50 40 30 20 10
出栈后栈是否为空?是
实际存储,如果不打印则表示正确:
空栈
现在是空栈,出栈操作失败
销毁栈后top指针:0000000000000000
再次初始化后,栈的宽度是否为空: 是
linked_stack_with_head.h
c
#pragma once
#include "stdio.h"
#include <stdbool.h>
typedef int ElemType;
// 结点结构
typedef struct LinkNode {
ElemType data; // 数据域
struct LinkNode* next; // 指针域
}LinkNode;
// 链栈结构
typedef struct {
LinkNode* top; // 栈顶元素
int length; // 链栈长度
}LinkStack;
// 初始化
void InitLStack(LinkStack* S);
// 销毁栈
void DestroyLStack(LinkStack* S);
// 清空
void ClearLStack(LinkStack* S);
// 进栈
bool PushLStack(LinkStack* S, ElemType x);
// 出栈
bool PopLStack(LinkStack* S, ElemType* x);
// 获取栈顶元素
bool GetTopLStack(LinkStack* S, ElemType* x);
// 判断栈空
bool IsEmptyLStack(LinkStack* S);
// 获取栈的大小
int GetLengthLStack(LinkStack* S);
// 遍历打印
void PrintLStack(LinkStack* S);
linked_stack_with_head.c
c
#include "linked_stack_with_head.h"
#include "stdio.h"
#include "stdlib.h"
#include "stdbool.h"
// 初始化(带头结点)
void InitLStack(LinkStack* S) {
// 创建头结点
S->top = (LinkNode*)malloc(sizeof(LinkNode));
if (!S->top) return;
// 创建空栈 --> 即第一个有效结点指针为空,栈宽0
S->top->next = NULL; // 空栈的情况下,top指向空
S->length = 0; // 空栈的情况下,链栈宽为0
}
// 销毁栈
void DestroyLStack(LinkStack* S) {
ClearLStack(S);
// 在清空栈的基础之上,做释放栈顶指针的操作
free(S->top); // 释放头结点
S->top = NULL; // 栈顶指针置空
S->length = 0; // 栈宽0
}
// 清空
void ClearLStack(LinkStack* S) {
LinkNode* cur = S->top->next; // 存储第一个有效结点的地址
// 从第一个结点开始一直到最后一个结点一直释放空间
while (cur != NULL) {
LinkNode* next = cur->next;
free(cur);
cur = next;
}
S->top->next = NULL; // 第一个有效结点的指针置空
S->length = 0; // 链栈宽为0
}
// 进栈(有头结点)
bool PushLStack(LinkStack* S, ElemType x) {
LinkNode* NewNode = (LinkNode*)malloc(sizeof(LinkNode));
if (!NewNode) return false; // 内存分配失败
// 将新的结点插入到头结点和第1个有效结点之间
NewNode->data = x;
NewNode->next = S->top->next;
S->top->next = NewNode;
S->length++; // 长度++
return true;
}
// 出栈(带头结点)
bool PopLStack(LinkStack* S, ElemType* x) {
if (IsEmptyLStack(S)) return false; // 如果是空栈,返回false
LinkNode* OldTop = S->top->next; // 将第一个有效结点地址保护下来
*x = OldTop->data;
S->top->next = OldTop->next; // 将头结点后继指向原第2个结点
free(OldTop); // 释放原来的第一个结点
S->length--; // 长度--
return true;
}
// 获取栈顶元素
bool GetTopLStack(LinkStack* S, ElemType* x) {
if (IsEmptyLStack(S)) return false; // 空栈,返回false
*x = S->top->next->data; // 头结点后继结点是第一个结点
return true;
}
// 判断栈空
bool IsEmptyLStack(LinkStack* S) {
return S->top->next == NULL; // 如果头结点的后继结点指向NULL,则表示为栈空
}
// 获取栈的大小
int GetLengthLStack(LinkStack* S) {
return S->length;
}
// 遍历打印
void PrintLStack(LinkStack* S) {
if (IsEmptyLStack(S)) {
printf("空栈\n");
return;
}
LinkNode* cur = S->top->next; // 从第一个有效结点开始
printf("栈顶(头结点) -> ");
while (cur != NULL) { // 如果是NULL,表示当前位置是最后结点的后继,则停止循环
printf("%d -> ", cur->data);
cur = cur->next;
}
printf("栈底\n");
}