数据结构C语言练习(栈)

一、引言

LeetCode 20 题 "有效的括号" 中,我们需要判断一个只包含 {} [] () 的字符串是否有效。本文通过实现一个动态栈结构,详细讲解代码逻辑,帮助理解栈在括号匹配问题中的应用。


二、栈结构核心函数讲解

1. 栈的初始化:StackInit

复制代码
void StackInit(Stack* ps) {
    ps->_a = NULL;
    ps->_top = ps->_capacity = 0;
}
  • 作用:初始化栈的成员变量。
  • 细节 :将存储数据的指针 _a 置为空,栈顶 _top 和容量 _capacity 初始化为 0,为后续操作做准备。

2. 获取栈顶元素:StackTop

复制代码
STDataType StackTop(Stack* ps) {
    return ps->_a[ps->_top - 1];
}
  • 作用:返回栈顶元素。
  • 细节 :通过 _top - 1 定位栈顶元素(因为 _top 指向栈顶下一个位置),使用前需确保栈非空。

3. 入栈操作:StackPush

复制代码
void StackPush(Stack* ps, STDataType data) {
    assert(ps);
    if (ps->_top == ps->_capacity) {
        int newCapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
        STDataType* tmp = (STDataType*)realloc(ps->_a, newCapacity * sizeof(STDataType));
        if (tmp == NULL) {
            perror("realloc fail");
            exit(1);
        }
        ps->_a = tmp;
        ps->_capacity = newCapacity;
    }
    ps->_a[ps->_top++] = data;
}
  • 作用:向栈中插入元素,支持动态扩容。
  • 细节
    • 先检查栈空间是否不足,不足则用 realloc 扩容(初始容量 4,之后双倍扩容)。
    • 插入元素后,_top 自增指向下一个位置。

4. 检查栈是否为空:StackEmpty

复制代码
bool StackEmpty(Stack* ps) {
    assert(ps);
    return ps->_top == 0;
}
  • 作用:判断栈是否为空。
  • 细节 :通过 _top 是否为 0 判断,是则返回 true,否则 false

5. 出栈操作:StackPop

复制代码
void StackPop(Stack* ps) {
    assert(!StackEmpty(ps));
    ps->_top--;
}
  • 作用:删除栈顶元素。
  • 细节 :先通过 assert 确保栈非空,再将 _top 减 1,实现逻辑删除。

6. 销毁栈:StackDestroy

复制代码
void StackDestroy(Stack* ps) {
    if (ps->_a) {
        free(ps->_a);
    }
    ps->_a = NULL;
    ps->_top = ps->_capacity = 0;
}
  • 作用:释放栈占用的内存,防止内存泄漏。
  • 细节 :先释放动态分配的数组 _a,再将成员变量重置为初始状态。

三、核心逻辑:isValid 函数

复制代码
bool isValid(char* s) {
    Stack st;
    StackInit(&st);
    char* ps = s;
    while (*ps != '\0') {
        if (*ps == '(' || *ps == '[' || *ps == '{') {
            StackPush(&st, *ps);
        } else {
            if (StackEmpty(&st)) {
                StackDestroy(&st);
                return false;
            }
            char top = StackTop(&st);
            if ((top == '(' && *ps == ')') || (top == '[' && *ps == ']') || (top == '{' && *ps == '}')) {
                StackPop(&st);
            } else {
                StackDestroy(&st);
                return false;
            }
        }
        ps++;
    }
    bool ret = StackEmpty(&st);
    StackDestroy(&st);
    return ret;
}
  • 逻辑解析
    1. 初始化栈 :创建栈 st 并初始化。
    2. 遍历字符串
      • 遇到左括号 ({[,直接入栈。
      • 遇到右括号 )}]
        • 若栈为空,说明无匹配左括号,直接返回 false
        • 检查栈顶元素是否匹配当前右括号,匹配则出栈,不匹配返回 false
    3. 最终检查:遍历结束后,若栈为空,说明所有括号匹配,否则不匹配。
    4. 销毁栈:无论结果如何,最后销毁栈释放资源。

四、完整代码

复制代码
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef char STDataType;
typedef struct Stack {
    STDataType* _a;
    int _top;
    int _capacity;
} Stack;

void StackInit(Stack* ps) {
    ps->_a = NULL;
    ps->_top = ps->_capacity = 0;
}

STDataType StackTop(Stack* ps) {
    return ps->_a[ps->_top - 1];
}

void StackPush(Stack* ps, STDataType data) {
    assert(ps);
    if (ps->_top == ps->_capacity) {
        int newCapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
        STDataType* tmp = (STDataType*)realloc(ps->_a, newCapacity * sizeof(STDataType));
        if (tmp == NULL) {
            perror("realloc fail");
            exit(1);
        }
        ps->_a = tmp;
        ps->_capacity = newCapacity;
    }
    ps->_a[ps->_top++] = data;
}

bool StackEmpty(Stack* ps) {
    assert(ps);
    return ps->_top == 0;
}

void StackPop(Stack* ps) {
    assert(!StackEmpty(ps));
    ps->_top--;
}

void StackDestroy(Stack* ps) {
    if (ps->_a) {
        free(ps->_a);
    }
    ps->_a = NULL;
    ps->_top = ps->_capacity = 0;
}

bool isValid(char* s) {
    Stack st;
    StackInit(&st);
    char* ps = s;
    while (*ps != '\0') {
        if (*ps == '(' || *ps == '[' || *ps == '{') {
            StackPush(&st, *ps);
        } else {
            if (StackEmpty(&st)) {
                StackDestroy(&st);
                return false;
            }
            char top = StackTop(&st);
            if ((top == '(' && *ps == ')') || (top == '[' && *ps == ']') || (top == '{' && *ps == '}')) {
                StackPop(&st);
            } else {
                StackDestroy(&st);
                return false;
            }
        }
        ps++;
    }
    bool ret = StackEmpty(&st);
    StackDestroy(&st);
    return ret;
}

五、总结

通过实现动态栈,我们高效解决了括号匹配问题。栈的 "后进先出" 特性完美契合括号匹配逻辑:左括号入栈,右括号与栈顶匹配。这一思路可扩展到更多需要匹配关系的场景(如标签匹配、表达式校验等)。理解栈的底层实现,能更灵活地应用这一数据结构解决实际问题。

相关推荐
程序员黄同学8 分钟前
动态规划,如何应用动态规划解决实际问题?
算法·动态规划
march_birds26 分钟前
FreeRTOS 与 RT-Thread 事件组对比分析
c语言·单片机·算法·系统架构
小麦嵌入式1 小时前
Linux驱动开发实战(十一):GPIO子系统深度解析与RGB LED驱动实践
linux·c语言·驱动开发·stm32·嵌入式硬件·物联网·ubuntu
斯汤雷1 小时前
Matlab绘图案例,设置图片大小,坐标轴比例为黄金比
数据库·人工智能·算法·matlab·信息可视化
云 无 心 以 出 岫1 小时前
贪心算法QwQ
数据结构·c++·算法·贪心算法
俏布斯2 小时前
算法日常记录
java·算法·leetcode
独好紫罗兰2 小时前
洛谷题单3-P5719 【深基4.例3】分类平均-python-流程图重构
开发语言·python·算法
jelasin2 小时前
LibCoroutine开发手记:细粒度C语言协程库
c语言
篝火悟者2 小时前
自学-C语言-基础-数组、函数、指针、结构体和共同体、文件
c语言·开发语言
SheepMeMe2 小时前
蓝桥杯2024省赛PythonB组——日期问题
python·算法·蓝桥杯