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 的适配实现泛用性,在实际开发中可根据需求扩展更多操作(如栈元素遍历、批量入栈等)。链式栈的内存管理需特别注意,确保所有节点都能被正确释放,避免内存泄漏。

相关推荐
虾..1 小时前
Linux 进程替换
linux·运维·服务器
赖small强1 小时前
【Linux驱动开发】Linux Debugfs 虚拟文件系统深度解析与实战指南
linux·驱动开发·debugfs
Crazy________1 小时前
43ansible常用模块及变量定义方式
linux·运维·服务器
小白电脑技术1 小时前
飞牛NAS最近好奇怪啊?一段时间不重启,内存几乎快用完了?
linux·电脑
幸运小猿1 小时前
启动项目报错,zookeeper影响的
linux·运维·服务器
意法半导体STM321 小时前
STM32N6 如何配置EMMC启动 LAT1581
stm32·单片机·嵌入式硬件·mcu·嵌入式ai·stm32n6·stm32开发
liu****1 小时前
11.字符函数和字符串函数(一)
linux·运维·c语言·开发语言·数据结构·算法
CC.GG1 小时前
【C++】面向对象三大特性之一——继承
java·数据库·c++
逆小舟1 小时前
【STM32】IIC→OLED显示
stm32·单片机·嵌入式硬件