目录
一、链栈基础结构
链栈是用链表实现的栈(先进后出,LIFO),核心结构是StackNode
节点和LinkStack
栈顶指针:
typedef struct StackNode {
SElemType data; // 数据域:存储栈元素
struct StackNode *next; // 指针域:指向后一个节点(栈底方向)
} StackNode, *LinkStack; // LinkStack本质是指向StackNode的指针(栈顶指针)
- 栈顶指针
S
指向栈中最顶部的节点(最后入栈的元素) - 空栈时,
S = NULL
(无任何节点) - 元素入栈 / 出栈仅在栈顶操作(链表头部),符合栈 "先进后出" 特性
二、核心操作算法详解
1. 初始化栈(InitStack)
功能:创建一个空栈算法步骤:
- 将栈顶指针
S
直接置为NULL
- 此时栈中无任何节点,即为空栈
代码解析:
void InitStack(LinkStack &S) {
S = NULL; // 栈顶指针置空,代表空栈
}
复杂度:
- 时间复杂度:O (1)(仅需一次指针赋值)
- 空间复杂度:O (1)(无额外空间占用)注意:初始化后必须调用此函数,否则栈顶指针可能为随机值,导致后续操作出错。
2. 判断栈是否为空(StackEmpty)
功能:检查栈中是否有元素算法步骤:
- 检查栈顶指针
S
是否为NULL
- 若
S == NULL
,返回true
(空栈);否则返回false
(非空栈)
代码解析:
bool StackEmpty(LinkStack S) {
return (S == NULL); // 栈顶指针为空 → 空栈
}
复杂度:
- 时间复杂度:O (1)(仅需一次指针比较)
- 空间复杂度:O (1)注意:几乎所有栈操作前都需要先判断栈是否为空(如出栈、取栈顶),避免对空栈操作导致错误。
3. 求栈的长度(StackLength)
功能:计算栈中元素的个数算法步骤:
- 初始化计数器
length = 0
,创建临时指针p
并指向栈顶S
- 循环遍历链表:当
p != NULL
时,计数器length++
,p
移动到下一个节点(p = p->next
) - 遍历结束后,
length
即为栈的长度
代码解析:
int StackLength(LinkStack S) {
int length = 0;
LinkStack p = S; // 临时指针,从栈顶开始遍历
while (p != NULL) {
length++; // 计数当前节点
p = p->next; // 移动到下一个节点(栈底方向)
}
return length;
}
复杂度:
- 时间复杂度:O (n)(n 为栈长度,需遍历所有节点)
- 空间复杂度:O (1)(仅用一个临时指针)注意:链栈无法像顺序栈那样直接通过数组下标获取长度,必须遍历链表。
4. 进栈操作(Push)
功能:将元素e
插入栈顶(成为新的栈顶元素)算法步骤:
- 创建新节点
p
(new StackNode
) - 新节点数据域赋值:
p->data = e
- 新节点指针域指向原栈顶:
p->next = S
(原栈顶成为新节点的下一个节点) - 更新栈顶指针:
S = p
(新节点成为新的栈顶)
代码解析:
void Push(LinkStack &S, SElemType e) {
LinkStack p = new StackNode; // 1. 创建新节点
p->data = e; // 2. 存入数据
p->next = S; // 3. 新节点指向原栈顶
S = p; // 4. 更新栈顶为新节点
}
示意图:
原栈:S → A → B → NULL(栈顶为A)
进栈e后:
S → e → A → B → NULL(栈顶为e)
复杂度:
- 时间复杂度:O (1)(仅需 4 步指针操作,无需遍历)
- 空间复杂度:O (1)(仅创建一个新节点)注意:进栈是链栈的核心操作,利用链表头部插入的特性实现,效率极高。
5. 出栈操作(Pop)
功能:移除栈顶元素,并返回该元素的值算法步骤:
- 先判断栈是否为空(
StackEmpty(S)
):若为空,输出错误信息,返回特殊值(如 - 1) - 若栈非空,创建临时指针
p
指向栈顶S
(暂存栈顶节点) - 取出栈顶元素值:
e = p->data
- 更新栈顶指针:
S = S->next
(原栈顶的下一个节点成为新栈顶) - 释放原栈顶节点的内存(
delete p
),避免内存泄漏 - 返回取出的元素值
e
代码解析:
SElemType Pop(LinkStack &S) {
if (StackEmpty(S)) { // 1. 检查空栈
cerr << "错误:栈为空,无法执行出栈操作!" << endl;
return -1;
}
LinkStack p = S; // 2. 暂存栈顶节点
SElemType e = p->data;// 3. 取出栈顶元素
S = S->next; // 4. 更新栈顶指针
delete p; // 5. 释放内存
return e; // 6. 返回元素
}
示意图:
原栈:S → e → A → B → NULL(栈顶为e)
出栈后:
S → A → B → NULL(栈顶为A,e被移除并返回)
复杂度:
- 时间复杂度:O (1)(仅需几步指针操作)
- 空间复杂度:O (1)(仅用一个临时指针)注意:出栈必须释放原栈顶节点的内存,否则会导致内存泄漏;空栈时必须报错,避免访问空指针。
6. 取栈顶元素(GetTop)
功能:获取栈顶元素的值(不改变栈结构,元素仍在栈中)算法步骤:
- 判断栈是否为空:若为空,输出错误信息,返回特殊值
- 若栈非空,直接返回栈顶节点的数据(
S->data
)
代码解析:
SElemType GetTop(LinkStack S) {
if (StackEmpty(S)) { // 检查空栈
cerr << "错误:栈为空,无栈顶元素!" << endl;
return -1;
}
return S->data; // 返回栈顶元素值
}
复杂度:
- 时间复杂度:O (1)(直接访问栈顶节点)
- 空间复杂度:O (1)与出栈的区别:取栈顶仅读取元素,不改变栈结构;出栈会移除元素并修改栈结构。
7. 清空栈(ClearStack)
功能:删除栈中所有元素,但保留栈的结构(最终为一个空栈)算法步骤:
- 创建两个临时指针
p
和q
,p
初始指向栈顶S
- 循环遍历所有节点:
q = p
(暂存当前节点)p = p->next
(p
移动到下一个节点)delete q
(释放当前节点内存)
- 遍历结束后,将栈顶指针
S
置为NULL
(此时栈为空)
代码解析:
void ClearStack(LinkStack &S) {
LinkStack p, q;
p = S;
while (p != NULL) {
q = p; // 暂存当前节点
p = p->next; // 移动到下一个节点
delete q; // 释放当前节点
}
S = NULL; // 栈顶指针置空,成为空栈
}
复杂度:
- 时间复杂度:O (n)(需遍历并释放所有节点)
- 空间复杂度:O (1)(仅用两个临时指针)与销毁栈的区别:清空栈后,栈仍可继续使用(可重新进栈);销毁栈的逻辑与清空栈一致(链栈无额外结构),但语义上代表栈生命周期结束。
8. 销毁栈(DestroyStack)
功能:释放栈占用的所有内存,结束栈的生命周期算法步骤:
- 直接调用
ClearStack(S)
(释放所有节点内存,并将S
置为NULL
) - 链栈无额外的容器结构(如顺序栈的数组),因此无需其他操作
代码解析:
void DestroyStack(LinkStack &S) {
ClearStack(S); // 释放所有节点,栈顶指针置空
}
复杂度:
- 时间复杂度:O (n)(同 ClearStack)
- 空间复杂度:O (1)注意:销毁后若需使用栈,必须重新调用
InitStack
初始化。
9. 遍历栈(TraverseStack)
功能:从栈顶到栈底依次打印所有元素算法步骤:
- 判断栈是否为空:若为空,输出提示信息
- 若栈非空,创建临时指针
p
指向栈顶S
- 循环遍历:当
p != NULL
时,打印p->data
,p
移动到下一个节点(p = p->next
) - 遍历结束后换行
代码解析:
void TraverseStack(LinkStack S) {
if (StackEmpty(S)) {
cout << "栈为空,无元素可遍历!" << endl;
return;
}
cout << "链栈元素(从栈顶到栈底):";
LinkStack p = S;
while (p != NULL) {
cout << p->data << " "; // 打印当前元素
p = p->next; // 移动到下一个节点(栈底方向)
}
cout << endl;
}
示例:栈中元素(从栈顶到栈底)为50,40,30,20,10
,遍历后输出:链栈元素(从栈顶到栈底):50 40 30 20 10
复杂度:
- 时间复杂度:O (n)(需遍历所有节点)
- 空间复杂度:O (1)
10. 复制栈(CopyStack)
功能:将栈S
的所有元素复制到栈T
,复制后T
与S
的元素顺序完全一致(栈顶到栈底元素相同)算法步骤:
- 若目标栈
T
非空,先调用ClearStack(T)
清空T
(避免内存泄漏) - 若源栈
S
为空,将T
置为NULL
,返回true
- 若
S
非空,创建辅助栈temp
并初始化 - 遍历源栈
S
,将所有元素依次压入辅助栈temp
(此时temp
的元素是S
的逆序,因为栈是先进后出) - 遍历辅助栈
temp
,将所有元素依次弹出并压入目标栈T
(此时T
的元素顺序与S
完全一致) - 返回
true
代码解析:
bool CopyStack(LinkStack S, LinkStack &T) {
if (T != NULL) {
ClearStack(T); // 1. 先清空目标栈
}
if (StackEmpty(S)) { // 2. 源栈为空,目标栈也为空
T = NULL;
return true;
}
LinkStack temp;
InitStack(temp); // 3. 创建辅助栈
LinkStack p = S;
// 4. 将S的元素逆序存入temp
while (p != NULL) {
Push(temp, p->data);
p = p->next;
}
// 5. 将temp的元素弹出并存入T,恢复原顺序
while (!StackEmpty(temp)) {
Push(T, Pop(temp));
}
return true;
}
示意图:
源栈S:顶→50→40→30→底
辅助栈temp:先存入50→40→30 → temp中顺序为顶→30→40→50→底
目标栈T:从temp弹出30→40→50并压入 → T中顺序为顶→50→40→30→底(与S一致)
复杂度:
- 时间复杂度:O (n)(遍历
S
和temp
各一次,共 2n 步) - 空间复杂度:O (n)(辅助栈
temp
需存储 n 个元素)关键逻辑:利用辅助栈反转顺序,确保复制后的栈与原栈元素顺序一致(若直接复制链表,T
的元素会是S
的逆序,不符合栈的特性)。
11. 判断两个栈是否相等(StackEqual)
功能:检查两个栈S
和T
是否完全相等(元素数量相同,且对应位置元素的值相同)算法步骤:
- 调用
StackLength
分别获取S
和T
的长度,若长度不同,直接返回false
- 若长度相同,创建两个临时指针
p
(指向S
顶)和q
(指向T
顶) - 循环遍历两个栈:
- 若
p->data != q->data
,返回false
p
和q
分别移动到下一个节点(p = p->next
,q = q->next
)
- 若
- 遍历结束后,返回
true
代码解析:
bool StackEqual(LinkStack S, LinkStack T) {
// 1. 长度不同则不相等
if (StackLength(S) != StackLength(T)) {
return false;
}
LinkStack p = S, q = T;
// 2. 逐个比较元素
while (p != NULL && q != NULL) {
if (p->data != q->data) {
return false;
}
p = p->next;
q = q->next;
}
return true;
}
复杂度:
- 时间复杂度:O (n)(求长度需 O (n),比较元素需 O (n),总 O (n))
- 空间复杂度:O (1)(仅用两个临时指针)
12. 逆置栈(ReverseStack)
功能:反转栈中元素的顺序(原栈顶元素变为栈底,原栈底元素变为栈顶)算法步骤:
- 若栈为空(
StackEmpty(S)
)或只有 1 个元素(StackLength(S) == 1
),无需逆置,直接返回 - 若栈有多个元素,创建三个指针:
prev = NULL
(前一个节点)、curr = S
(当前节点)、next = NULL
(下一个节点) - 循环反转指针方向:
next = curr->next
(保存当前节点的下一个节点)curr->next = prev
(当前节点的指针改为指向前一个节点)prev = curr
(prev
移动到当前节点)curr = next
(curr
移动到下一个节点)
- 循环结束后,
prev
指向原栈底节点(新栈顶),更新栈顶指针S = prev
代码解析:
void ReverseStack(LinkStack &S) {
// 1. 空栈或单元素栈无需逆置
if (StackEmpty(S) || StackLength(S) == 1) {
return;
}
LinkStack prev = NULL;
LinkStack curr = S;
LinkStack next = NULL;
// 2. 反转链表指针方向
while (curr != NULL) {
next = curr->next; // 保存下一个节点
curr->next = prev; // 反转当前节点指针
prev = curr; // 移动prev
curr = next; // 移动curr
}
S = prev; // 3. 更新栈顶为原栈底
}
示意图:
原栈:S→50→40→30→20→10→NULL(顶→50,底→10)
反转指针后:
10→20→30→40→50→NULL,S指向10
逆置后栈:S→10→20→30→40→50→NULL(顶→10,底→50)
复杂度:
- 时间复杂度:O (n)(需遍历所有节点一次)
- 空间复杂度:O (1)(仅用三个临时指针,原地反转)优势:相比 "用辅助栈逆置"(空间 O (n)),此算法通过反转链表指针实现原地逆置,空间效率更高。
三、链栈的基本操作的完整代码展示
(一)C++代码
#include <iostream>
#include <windows.h> // 引入Windows系统头文件
using namespace std;
// 定义栈元素类型
typedef int SElemType;
// 链栈节点结构定义
typedef struct StackNode {
SElemType data; // 数据域
struct StackNode *next; // 指针域,指向后继节点
} StackNode, *LinkStack;
// 函数声明
void InitStack(LinkStack &S); // 链栈的初始化
bool StackEmpty(LinkStack S); // 判断链栈是否为空
int StackLength(LinkStack S); // 求链栈的长度
void Push(LinkStack &S, SElemType e); // 链栈进栈操作
SElemType Pop(LinkStack &S); // 链栈出栈操作
SElemType GetTop(LinkStack S); // 取链栈栈顶元素
void ClearStack(LinkStack &S); // 清空链栈(保留栈结构)
void DestroyStack(LinkStack &S); // 销毁链栈(释放所有资源)
void TraverseStack(LinkStack S); // 遍历链栈元素(从栈顶到栈底)
bool CopyStack(LinkStack S, LinkStack &T); // 复制栈(将S复制到T)
bool StackEqual(LinkStack S, LinkStack T); // 判断两个栈是否相等
void ReverseStack(LinkStack &S); // 逆置链栈
// 测试函数
int main() {
SetConsoleOutputCP(CP_UTF8); // 强制控制台使用UTF-8解析输出
LinkStack S, T;
// 初始化栈
InitStack(S);
InitStack(T);
cout << "初始化链栈S后,栈是否为空:" << (StackEmpty(S) ? "是" : "否") << endl;
// 进栈操作
cout << "\n执行进栈操作:依次入栈 10, 20, 30, 40, 50" << endl;
Push(S, 10);
Push(S, 20);
Push(S, 30);
Push(S, 40);
Push(S, 50);
TraverseStack(S);
cout << "栈S的长度:" << StackLength(S) << endl;
// 复制栈操作
cout << "\n复制栈S到栈T..." << endl;
CopyStack(S, T);
cout << "栈T的元素:";
TraverseStack(T);
cout << "栈S和栈T是否相等:" << (StackEqual(S, T) ? "是" : "否") << endl;
// 逆置栈操作
cout << "\n逆置栈S..." << endl;
ReverseStack(S);
cout << "逆置后栈S的元素:";
TraverseStack(S);
cout << "栈S和栈T是否相等:" << (StackEqual(S, T) ? "是" : "否") << endl;
// 再次逆置恢复原顺序
ReverseStack(S);
cout << "再次逆置后栈S的元素:";
TraverseStack(S);
// 出栈和取栈顶操作
cout << "\n执行出栈操作:" << endl;
cout << "出栈元素:" << Pop(S) << endl;
cout << "出栈元素:" << Pop(S) << endl;
TraverseStack(S);
cout << "当前栈顶元素:" << GetTop(S) << endl;
cout << "当前栈S的长度:" << StackLength(S) << endl;
// 清空栈操作
cout << "\n清空栈S..." << endl;
ClearStack(S);
cout << "清空后栈S是否为空:" << (StackEmpty(S) ? "是" : "否") << endl;
cout << "清空后栈S的长度:" << StackLength(S) << endl;
// 测试栈T
cout << "\n栈T的元素:";
TraverseStack(T);
cout << "栈T的长度:" << StackLength(T) << endl;
// 销毁栈
cout << "\n销毁链栈..." << endl;
DestroyStack(S);
DestroyStack(T);
cout << "销毁后,栈S是否为空:" << (StackEmpty(S) ? "是" : "否") << endl;
cout << "销毁后,栈T是否为空:" << (StackEmpty(T) ? "是" : "否") << endl;
return 0;
}
// 链栈的初始化
void InitStack(LinkStack &S) {
S = NULL; // 栈顶指针置空,表示空栈
}
// 判断链栈是否为空
bool StackEmpty(LinkStack S) {
return (S == NULL); // 栈顶指针为空则栈空
}
// 求链栈的长度
int StackLength(LinkStack S) {
int length = 0;
LinkStack p = S;
while (p != NULL) {
length++;
p = p->next;
}
return length;
}
// 链栈进栈操作
void Push(LinkStack &S, SElemType e) {
LinkStack p = new StackNode; // 创建新节点
p->data = e; // 存入数据
p->next = S; // 新节点指向原栈顶
S = p; // 更新栈顶指针为新节点
}
// 链栈出栈操作
SElemType Pop(LinkStack &S) {
if (StackEmpty(S)) {
cerr << "错误:栈为空,无法执行出栈操作!" << endl;
return -1; // 栈空时返回特殊值
}
LinkStack p = S; // 暂存栈顶节点
SElemType e = p->data; // 获取栈顶元素值
S = S->next; // 更新栈顶指针
delete p; // 释放原栈顶节点内存
return e; // 返回出栈元素
}
// 取链栈栈顶元素
SElemType GetTop(LinkStack S) {
if (StackEmpty(S)) {
cerr << "错误:栈为空,无栈顶元素!" << endl;
return -1; // 栈空时返回特殊值
}
return S->data; // 返回栈顶元素值
}
// 清空链栈(保留栈结构,仅清空元素)
void ClearStack(LinkStack &S) {
LinkStack p, q;
p = S;
while (p != NULL) {
q = p;
p = p->next;
delete q;
}
S = NULL; // 栈顶指针置空
}
// 销毁链栈(释放所有资源)
void DestroyStack(LinkStack &S) {
ClearStack(S); // 清空栈元素
// 链栈无需额外销毁操作,因为栈本身是指针
}
// 遍历链栈元素(从栈顶到栈底)
void TraverseStack(LinkStack S) {
if (StackEmpty(S)) {
cout << "栈为空,无元素可遍历!" << endl;
return;
}
cout << "链栈元素(从栈顶到栈底):";
LinkStack p = S;
while (p != NULL) {
cout << p->data << " ";
p = p->next;
}
cout << endl;
}
// 复制栈(将S复制到T)
bool CopyStack(LinkStack S, LinkStack &T) {
if (T != NULL) {
ClearStack(T); // 先清空目标栈
}
if (StackEmpty(S)) {
T = NULL;
return true;
}
// 辅助栈用于临时存储元素(解决顺序问题)
LinkStack temp;
InitStack(temp);
LinkStack p = S;
// 先将S的元素逆序存入临时栈
while (p != NULL) {
Push(temp, p->data);
p = p->next;
}
// 再将临时栈的元素存入T,恢复原顺序
while (!StackEmpty(temp)) {
Push(T, Pop(temp));
}
return true;
}
// 判断两个栈是否相等(元素数量和对应位置元素都相同)
bool StackEqual(LinkStack S, LinkStack T) {
// 长度不同则直接不相等
if (StackLength(S) != StackLength(T)) {
return false;
}
LinkStack p = S, q = T;
while (p != NULL && q != NULL) {
if (p->data != q->data) {
return false;
}
p = p->next;
q = q->next;
}
return true;
}
// 逆置链栈(改变元素顺序)
void ReverseStack(LinkStack &S) {
if (StackEmpty(S) || StackLength(S) == 1) {
return; // 空栈或只有一个元素无需逆置
}
LinkStack prev = NULL;
LinkStack curr = S;
LinkStack next = NULL;
// 反转链表指针方向
while (curr != NULL) {
next = curr->next; // 保存下一个节点
curr->next = prev; // 反转当前节点指针
prev = curr; // 移动prev到当前节点
curr = next; // 移动curr到下一个节点
}
S = prev; // 更新栈顶指针
}
(二)Python代码
# 定义栈节点类
class StackNode:
def __init__(self, data):
self.data = data # 数据域
self.next = None # 指针域,指向后继节点
# 链栈初始化:返回空栈(None表示空栈)
def init_stack():
return None
# 判断链栈是否为空
def stack_empty(s):
return s is None
# 求链栈的长度
def stack_length(s):
length = 0
p = s
while p is not None:
length += 1
p = p.next
return length
# 链栈进栈操作
def push(s, e):
# 创建新节点
p = StackNode(e)
# 新节点指向原栈顶
p.next = s
# 更新栈顶指针为新节点
return p
# 链栈出栈操作
def pop(s):
if stack_empty(s):
print("错误:栈为空,无法执行出栈操作!", file=sys.stderr)
return None, s # 返回None表示出错,栈顶不变
# 暂存栈顶节点
p = s
# 获取栈顶元素值
e = p.data
# 更新栈顶指针
s = s.next
# Python会自动回收内存,无需手动释放
return e, s
# 取链栈栈顶元素
def get_top(s):
if stack_empty(s):
print("错误:栈为空,无栈顶元素!", file=sys.stderr)
return None
return s.data
# 清空链栈(保留栈结构,仅清空元素)
def clear_stack(s):
# 遍历并释放所有节点(Python中无需手动释放,只需将栈顶置空)
return None
# 销毁链栈(释放所有资源)
def destroy_stack(s):
# 链栈在Python中无需额外销毁操作,清空即可
return clear_stack(s)
# 遍历链栈元素(从栈顶到栈底)
def traverse_stack(s):
if stack_empty(s):
print("栈为空,无元素可遍历!")
return
print("链栈元素(从栈顶到栈底):", end="")
p = s
while p is not None:
print(p.data, end=" ")
p = p.next
print()
# 复制栈(将S复制到T)
def copy_stack(s):
t = init_stack()
if stack_empty(s):
return t
# 辅助栈用于临时存储元素(解决顺序问题)
temp = init_stack()
p = s
# 先将S的元素逆序存入临时栈
while p is not None:
temp = push(temp, p.data)
p = p.next
# 再将临时栈的元素存入T,恢复原顺序
while not stack_empty(temp):
val, temp = pop(temp)
t = push(t, val)
return t
# 判断两个栈是否相等(元素数量和对应位置元素都相同)
def stack_equal(s, t):
# 长度不同则直接不相等
if stack_length(s) != stack_length(t):
return False
p, q = s, t
while p is not None and q is not None:
if p.data != q.data:
return False
p = p.next
q = q.next
return True
# 逆置链栈(改变元素顺序)
def reverse_stack(s):
if stack_empty(s) or stack_length(s) == 1:
return s # 空栈或只有一个元素无需逆置
prev = None
curr = s
next_node = None
# 反转链表指针方向
while curr is not None:
next_node = curr.next # 保存下一个节点
curr.next = prev # 反转当前节点指针
prev = curr # 移动prev到当前节点
curr = next_node # 移动curr到下一个节点
return prev # 返回新的栈顶指针
# 测试函数
def main():
import sys
# 初始化栈
s = init_stack()
t = init_stack()
print(f"初始化链栈S后,栈是否为空:{'是' if stack_empty(s) else '否'}")
# 进栈操作
print("\n执行进栈操作:依次入栈 10, 20, 30, 40, 50")
s = push(s, 10)
s = push(s, 20)
s = push(s, 30)
s = push(s, 40)
s = push(s, 50)
traverse_stack(s)
print(f"栈S的长度:{stack_length(s)}")
# 复制栈操作
print("\n复制栈S到栈T...")
t = copy_stack(s)
print("栈T的元素:", end="")
traverse_stack(t)
print(f"栈S和栈T是否相等:{'是' if stack_equal(s, t) else '否'}")
# 逆置栈操作
print("\n逆置栈S...")
s = reverse_stack(s)
print("逆置后栈S的元素:", end="")
traverse_stack(s)
print(f"栈S和栈T是否相等:{'是' if stack_equal(s, t) else '否'}")
# 再次逆置恢复原顺序
s = reverse_stack(s)
print("再次逆置后栈S的元素:", end="")
traverse_stack(s)
# 出栈和取栈顶操作
print("\n执行出栈操作:")
val, s = pop(s)
print(f"出栈元素:{val}")
val, s = pop(s)
print(f"出栈元素:{val}")
traverse_stack(s)
print(f"当前栈顶元素:{get_top(s)}")
print(f"当前栈S的长度:{stack_length(s)}")
# 清空栈操作
print("\n清空栈S...")
s = clear_stack(s)
print(f"清空后栈S是否为空:{'是' if stack_empty(s) else '否'}")
print(f"清空后栈S的长度:{stack_length(s)}")
# 测试栈T
print("\n栈T的元素:", end="")
traverse_stack(t)
print(f"栈T的长度:{stack_length(t)}")
# 销毁栈
print("\n销毁链栈...")
s = destroy_stack(s)
t = destroy_stack(t)
print(f"销毁后,栈S是否为空:{'是' if stack_empty(s) else '否'}")
print(f"销毁后,栈T是否为空:{'是' if stack_empty(t) else '否'}")
if __name__ == "__main__":
main()
(三)Java代码
import java.util.Scanner;
public class LinkStack {
// 定义栈节点内部类
private static class StackNode {
int data; // 数据域
StackNode next; // 指针域,指向后继节点
// 节点构造函数
public StackNode(int data) {
this.data = data;
this.next = null;
}
}
private StackNode top; // 栈顶指针
// 链栈的初始化
public LinkStack() {
top = null; // 栈顶指针置空,表示空栈
}
// 判断链栈是否为空
public boolean stackEmpty() {
return top == null;
}
// 求链栈的长度
public int stackLength() {
int length = 0;
StackNode p = top;
while (p != null) {
length++;
p = p.next;
}
return length;
}
// 链栈进栈操作
public void push(int e) {
StackNode p = new StackNode(e); // 创建新节点
p.next = top; // 新节点指向原栈顶
top = p; // 更新栈顶指针为新节点
}
// 链栈出栈操作
public int pop() {
if (stackEmpty()) {
System.err.println("错误:栈为空,无法执行出栈操作!");
return -1; // 栈空时返回特殊值
}
StackNode p = top; // 暂存栈顶节点
int e = p.data; // 获取栈顶元素值
top = top.next; // 更新栈顶指针
p.next = null; // 帮助垃圾回收
return e; // 返回出栈元素
}
// 取链栈栈顶元素
public int getTop() {
if (stackEmpty()) {
System.err.println("错误:栈为空,无栈顶元素!");
return -1; // 栈空时返回特殊值
}
return top.data; // 返回栈顶元素值
}
// 清空链栈(保留栈结构,仅清空元素)
public void clearStack() {
top = null; // 栈顶指针置空,Java垃圾回收会自动清理节点
}
// 销毁链栈(释放所有资源)
public void destroyStack() {
clearStack(); // 清空栈元素,Java无需额外操作
}
// 遍历链栈元素(从栈顶到栈底)
public void traverseStack() {
if (stackEmpty()) {
System.out.println("栈为空,无元素可遍历!");
return;
}
System.out.print("链栈元素(从栈顶到栈底):");
StackNode p = top;
while (p != null) {
System.out.print(p.data + " ");
p = p.next;
}
System.out.println();
}
// 复制栈(将当前栈复制到目标栈)
public LinkStack copyStack() {
LinkStack target = new LinkStack();
if (stackEmpty()) {
return target;
}
// 辅助栈用于临时存储元素(解决顺序问题)
LinkStack temp = new LinkStack();
StackNode p = top;
// 先将当前栈的元素逆序存入临时栈
while (p != null) {
temp.push(p.data);
p = p.next;
}
// 再将临时栈的元素存入目标栈,恢复原顺序
while (!temp.stackEmpty()) {
target.push(temp.pop());
}
return target;
}
// 判断两个栈是否相等(元素数量和对应位置元素都相同)
public boolean stackEqual(LinkStack other) {
// 长度不同则直接不相等
if (this.stackLength() != other.stackLength()) {
return false;
}
StackNode p = this.top;
StackNode q = other.top;
while (p != null && q != null) {
if (p.data != q.data) {
return false;
}
p = p.next;
q = q.next;
}
return true;
}
// 逆置链栈(改变元素顺序)
public void reverseStack() {
if (stackEmpty() || stackLength() == 1) {
return; // 空栈或只有一个元素无需逆置
}
StackNode prev = null;
StackNode curr = top;
StackNode next = null;
// 反转链表指针方向
while (curr != null) {
next = curr.next; // 保存下一个节点
curr.next = prev; // 反转当前节点指针
prev = curr; // 移动prev到当前节点
curr = next; // 移动curr到下一个节点
}
top = prev; // 更新栈顶指针
}
// 测试函数
public static void main(String[] args) {
LinkStack S = new LinkStack();
LinkStack T = new LinkStack();
// 初始化栈
System.out.println("初始化链栈S后,栈是否为空:" + (S.stackEmpty() ? "是" : "否"));
// 进栈操作
System.out.println("\n执行进栈操作:依次入栈 10, 20, 30, 40, 50");
S.push(10);
S.push(20);
S.push(30);
S.push(40);
S.push(50);
S.traverseStack();
System.out.println("栈S的长度:" + S.stackLength());
// 复制栈操作
System.out.println("\n复制栈S到栈T...");
T = S.copyStack();
System.out.print("栈T的元素:");
T.traverseStack();
System.out.println("栈S和栈T是否相等:" + (S.stackEqual(T) ? "是" : "否"));
// 逆置栈操作
System.out.println("\n逆置栈S...");
S.reverseStack();
System.out.print("逆置后栈S的元素:");
S.traverseStack();
System.out.println("栈S和栈T是否相等:" + (S.stackEqual(T) ? "是" : "否"));
// 再次逆置恢复原顺序
S.reverseStack();
System.out.print("再次逆置后栈S的元素:");
S.traverseStack();
// 出栈和取栈顶操作
System.out.println("\n执行出栈操作:");
System.out.println("出栈元素:" + S.pop());
System.out.println("出栈元素:" + S.pop());
S.traverseStack();
System.out.println("当前栈顶元素:" + S.getTop());
System.out.println("当前栈S的长度:" + S.stackLength());
// 清空栈操作
System.out.println("\n清空栈S...");
S.clearStack();
System.out.println("清空后栈S是否为空:" + (S.stackEmpty() ? "是" : "否"));
System.out.println("清空后栈S的长度:" + S.stackLength());
// 测试栈T
System.out.print("\n栈T的元素:");
T.traverseStack();
System.out.println("栈T的长度:" + T.stackLength());
// 销毁栈
System.out.println("\n销毁链栈...");
S.destroyStack();
T.destroyStack();
System.out.println("销毁后,栈S是否为空:" + (S.stackEmpty() ? "是" : "否"));
System.out.println("销毁后,栈T是否为空:" + (T.stackEmpty() ? "是" : "否"));
}
}
四、程序运行结果展示
(一)C++程序运行截图
(二)Python程序运行截图
(三)Java程序运行截图
五、总结
链栈是一种基于链表实现的栈结构,具有先进后出(LIFO)的特性。其核心结构包括StackNode节点(含数据域和指针域)和栈顶指针LinkStack。主要操作包括初始化、判空、求长度、入栈、出栈、取栈顶元素等,时间复杂度多为O(1)。链栈的优势在于动态内存分配,无需预先确定容量,但需注意内存管理。此外,还支持高级操作如复制栈、判断栈相等、逆置栈等。本文提供了C++、Python和Java三种语言的完整实现代码及测试案例,展示了链栈的基本操作和运行结果。链栈适用于需要动态调整大小的场景,是数据结构中的重要实现方式。