23、链式栈(LinkStack)的实现与多场景应用

链式栈(LinkStack)的实现与多场景应用

一、链式栈的核心设计与实现

链式栈通过链表节点存储数据,避免了顺序栈的容量限制问题。其核心结构包括栈节点(存储数据和指针)和栈管理结构(维护栈顶指针和元素数量)。

1. 头文件定义(linkstack.h)

根据不同应用场景,DATATYPE 数据类型会进行适配,以下是基础结构定义:

c 复制代码
#ifndef _LINKSTACK_H_
#define _LINKSTACK_H_

// 栈节点结构
typedef struct stacknode
{
    DATATYPE data;           // 存储的数据
    struct stacknode *next;  // 指向栈底方向的节点
} LinkStackNode;

// 栈管理结构
typedef struct 
{
    LinkStackNode *top;      // 栈顶指针
    int clen;                // 栈中元素数量
} LinkStack;

// 栈操作函数声明
LinkStack* CreateLinkStack();       // 创建栈
int PushLinkStack(LinkStack*ls,DATATYPE*newdata);  // 入栈
int PopLinkStack(LinkStack*ls);     // 出栈(基础版)
DATATYPE* GetTopLinkStack(LinkStack*ls);  // 获取栈顶元素
int GetSizeLinkStack(LinkStack*ls);  // 获取栈大小
int IsEmptyLinkStack(LinkStack*ls);  // 判断栈是否为空
int DestroyLinkStack(LinkStack*ls);  // 销毁栈

#endif

2. 核心实现(linkstack.c)

链式栈的基本操作实现如下,重点关注内存管理和指针操作:

c 复制代码
#include "linkstack.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// 创建栈
LinkStack* CreateLinkStack()
{
    LinkStack* ls = (LinkStack*)malloc(sizeof(LinkStack));
    if (NULL == ls)
    {
        printf("CreateLinkStack: malloc failed\n");
        return NULL;
    }
    ls->top = NULL;  // 初始化栈顶为空
    ls->clen = 0;    // 初始元素数量为0
    return ls;
}

// 入栈操作
int PushLinkStack(LinkStack* ls, DATATYPE* newdata)
{
    if (NULL == ls || newdata == NULL)
    {
        printf("PushLinkStack: invalid parameter\n");
        return 1;
    }

    // 创建新节点
    LinkStackNode* newnode = (LinkStackNode*)malloc(sizeof(LinkStackNode));
    if (NULL == newnode)
    {
        printf("PushLinkStack: malloc node failed\n");
        return 1;
    }

    // 存储数据并更新指针
    memcpy(&newnode->data, newdata, sizeof(DATATYPE));
    newnode->next = ls->top;  // 新节点指向原栈顶
    ls->top = newnode;        // 栈顶指针指向新节点
    ls->clen++;
    return 0;
}

// 出栈操作
int PopLinkStack(LinkStack* ls)
{
    if (IsEmptyLinkStack(ls))
    {
        return 1;  // 栈空时出栈失败
    }
    LinkStackNode* tmp = ls->top;  // 暂存栈顶节点
    ls->top = ls->top->next;       // 栈顶指针下移
    free(tmp);                     // 释放原栈顶节点内存
    ls->clen--;
    return 0;
}

// 获取栈顶元素
DATATYPE* GetTopLinkStack(LinkStack* ls)
{
    if(IsEmptyLinkStack(ls))
    {
        return NULL;
    }
    return &ls->top->data;
}

// 销毁栈
int DestroyLinkStack(LinkStack *ls)
{
    // 依次弹出所有元素
    int len = GetSizeLinkStack(ls);
    for (int i = 0; i < len; i++)
    {
        PopLinkStack(ls);
    }
    free(ls);  // 释放栈管理结构
    return 0;
}

二、链式栈的多场景应用

1. 括号匹配检查工具

应用场景 :检查代码或文本中的括号(()[]{})是否匹配,包括类型匹配和位置追踪。

实现要点

  • 适配 DATATYPE 存储括号字符及位置信息
  • 遇到左括号时入栈,遇到右括号时与栈顶左括号匹配
  • 处理三种错误:右括号无匹配、括号类型不匹配、左括号未闭合
c 复制代码
// 括号检查专用DATATYPE定义
typedef struct {
    char bracket;  // 括号字符
    int line;      // 行号
    int col;       // 列号
} DATATYPE;

// 核心检查逻辑(main.c片段)
for (int i = 0; i < strlen(data); i++)
{
    char curr_char = data[i];
    // 处理行列号计算(略)

    if (is_left_bracket(curr_char)) 
    {
        // 左括号入栈,记录位置
        DATATYPE left_bracket;
        left_bracket.bracket = curr_char;
        left_bracket.line = line;
        left_bracket.col = col - 1;
        PushLinkStack(bracket_stack, &left_bracket);
    }
    else if (is_right_bracket(curr_char)) 
    {
        if (IsEmptyLinkStack(bracket_stack))
        {
            printf("错误:右括号无匹配,位置第%d行第%d列\n", line, col-1);
        }
        else
        {
            DATATYPE* top_left = GetTopLinkStack(bracket_stack);
            if (!is_bracket_match(top_left->bracket, curr_char))
            {
                printf("错误:类型不匹配,左括号'%c'在第%d行第%d列\n",
                       top_left->bracket, top_left->line, top_left->col);
            }
            PopLinkStack(bracket_stack);
        }
    }
}
// 检查剩余未闭合左括号(略)

2. 简单表达式计算器

应用场景 :计算只包含 +、-、*、/ 的整数表达式(如 2*3+5)。

实现要点

  • 使用两个栈:数字栈存储操作数,运算符栈存储运算符
  • DATATYPE 用联合体存储数字或运算符
  • 出栈时按"后入先算"原则计算结果
c 复制代码
// 表达式计算专用DATATYPE定义
typedef union {
    int num;    // 存储数字
    char op;    // 存储运算符
} DATATYPE;

// 计算逻辑(main.c片段)
while (!IsEmptyLinkStack(op_stack)) 
{
    // 弹出两个操作数和一个运算符
    DATATYPE num2_data, num1_data, op_data;
    PopLinkStack(num_stack, &num2_data);
    PopLinkStack(num_stack, &num1_data);
    PopLinkStack(op_stack, &op_data);

    // 计算并将结果入栈
    int result = calculate(num1_data.num, num2_data.num, op_data.op);
    DATATYPE res_data;
    res_data.num = result;
    PushLinkStack(num_stack, &res_data);
}

3. 人员信息管理

应用场景:通过栈实现人员信息的入栈、出栈和遍历。

实现要点

  • DATATYPE 定义为人员信息结构体
  • 展示栈的基本数据存储能力
c 复制代码
// 人员信息专用DATATYPE定义
typedef struct person
{
    char name[32];
    char sex;
    int age;
    int score;
} DATATYPE;

// 操作示例(main.c片段)
DATATYPE data[] = {
    {"zhangsan", 'f', 20, 80}, {"lisi", 'm', 21, 82},
    {"wangmazi", 'm', 22, 85}
};

// 入栈
for (int i = 0; i < 3; i++)
{
    PushLinkStack(ls, &data[i]);
}

// 出栈并打印
for (int i = 0; i < GetSizeLinkStack(ls); i++)
{
    DATATYPE tmp;
    memcpy(&tmp, GetTopLinkStack(ls), sizeof(DATATYPE));
    PopLinkStack(ls);
    printf("name:%s age:%d\n", tmp.name, tmp.age);
}

三、总结

链式栈通过动态链表实现,相比顺序栈更灵活,无需预先指定容量。本文通过三个案例展示了其扩展性:

  • 括号检查:利用栈的"后入先出"特性匹配成对元素
  • 表达式计算:通过双栈协同处理运算优先级(简化版)
  • 信息管理:作为通用数据容器存储结构化数据

核心设计技巧是通过 DATATYPE 的适配实现泛用性,在实际开发中可根据需求扩展更多操作(如栈元素遍历、批量入栈等)。链式栈的内存管理需特别注意,确保所有节点都能被正确释放,避免内存泄漏。

相关推荐
tokepson13 小时前
Mysql下载部署方法备份(Windows/Linux)
linux·服务器·windows·mysql
fqbqrr13 小时前
2601C++,cmake与导入
c++
fqbqrr14 小时前
2601C++,编写自己模块
c++
zz_nj15 小时前
工作的环境
linux·运维·服务器
极客先躯16 小时前
如何自动提取Git指定时间段的修改文件?Win/Linux双平台解决方案
linux·git·elasticsearch
ytttr87316 小时前
基于STM32和W5500芯片的Modbus TCP协议栈实现
stm32·嵌入式硬件
上大科技蔡生16 小时前
CS5715:2.7V~26V宽输入,单节锂电池适用,最高36V输出,省掉电感电流检测电阻,软启动时间可调,异步升压DCDC控制器
单片机·嵌入式硬件·dcdc
suijishengchengde16 小时前
****LINUX时间同步配置*****
linux·运维
CQ_YM17 小时前
51单片机(1)
单片机·嵌入式硬件·51单片机
qiuqyue17 小时前
基于虹软Linux Pro SDK的多路RTSP流并发接入、解码与帧级处理实践
linux·运维·网络