C语言栈刷题:LeetCode 20 有效的括号完整解题笔记

C语言栈刷题:LeetCode 20 有效的括号完整解题笔记

前言

LeetCode 20 是栈结构最经典入门题型,是理解后进先出LIFO特性的必练题目。括号天然的由内向外闭合规则完美适配栈逻辑,初学者极易出现空指针断言崩溃、内存泄漏、括号匹配错乱等问题。本文结合手写调试笔记,完整给出可落地的动态char栈实现、解题代码、易错点,适配期末复习、代码复盘、面试手写。

一、原题描述

给定只包含 ()[]{} 的字符串 s,判断括号是否有效。

有效必须全部满足三条规则:

  1. 左括号必须和同类型右括号闭合;
  2. 括号必须按照正确嵌套顺序闭合;
  3. 任意右括号都存在对应的同类型左括号。

测试样例汇总

输入 输出 说明
"()" true 正常成对匹配
"()[]{}" true 多类括号依次正常配对
"(]" false 左右括号类型不匹配
"([)]" false 嵌套顺序错乱,不符合闭合逻辑
"[" false 存在多余左括号,无闭合右括号
"]" false 开头就是右括号,无前置左括号

二、思路一:常规遍历暴力判断(不推荐)

时间复杂度:O(n2)O(n^2)O(n2) |空间复杂度:O(1)O(1)O(1)

解题逻辑

不断遍历字符串,成对删除匹配的括号,反复循环直到无法删除。若最终字符串清空则合法,否则非法。

优缺点

✅ 不需要栈结构,逻辑靠字符串替换实现

❌ 多层循环嵌套效率极低,代码繁琐,边界情况极多,不符合本题栈考察目标,面试完全不推荐


三、思路二:char类型动态栈(最优标准解法)

时间复杂度:O(n)O(n)O(n) |空间复杂度:O(n)O(n)O(n),栈存储左括号

栈结构整体设计

将栈存储元素改为char适配字符,采用动态数组实现,支持自动二倍扩容;完整封装初始化、销毁、入栈、出栈、取栈顶、判空全套接口。

指针分工规则
  • 遍历指针pi:逐个扫描整个字符串的每一个字符;
  • 栈:专门缓存所有读到的左括号,利用后进先出保证内层括号优先闭合。
执行流程规则
  1. 遍历字符为左括号( / [ / {:直接入栈保存;
  2. 遍历字符为右括号:
    ① 栈为空 → 无左括号可以匹配,直接判定失败;
    ② 读取栈顶左括号,和当前右括号类型不匹配 → 判定失败;
    ③ 匹配成功,弹出栈顶元素;
  3. 全部字符遍历完毕:栈必须为空。栈内剩余元素代表存在未闭合左括号,判定无效。

完整分层代码

1. 栈结构体与全部接口实现
c 复制代码
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>

typedef char STDataType;
typedef struct Stack
{
    STDataType* arr;
    int top;
    int capacity;
}ST;

//初始化
void StackInit(ST* ps)
{
	assert(ps);
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}
//销毁栈,释放堆内存
void StackDestroy(ST* ps)
{
	assert(ps);
	if (ps->arr)
		free(ps->arr);
	ps->arr = NULL;
	ps->top = ps->capacity = 0;
}
//入栈
void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		//空栈初始分配4,后续二倍扩容
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
	ps->arr[ps->top++] = x;
}
//判断栈是否为空
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
}
//出栈
void StackPop(ST* ps)
{
	assert(!StackEmpty(ps));
	--ps->top;
}
//获取栈顶元素
STDataType StackTop(ST* ps)
{
	assert(!StackEmpty(ps));
	return ps->arr[ps->top - 1];
}
//获取栈有效元素数量
int StackSize(ST* ps)
{
	return ps->top;
}
2. 核心校验函数 isValid
c 复制代码
bool isValid(char* s) {
    ST st;
    StackInit(&st);
    char* pi=s;
    while(*pi!='\0')
    {
        if(*pi=='('||*pi=='['||*pi=='{')
        {
            StackPush(&st,*pi);
        }
        else{
            //右括号出现,栈空直接失败
            if(StackEmpty(&st))
            {
                StackDestroy(&st);
                return false;
            }
            char top=StackTop(&st);
            //三类括号任意一组不匹配则返回false
            if(top=='(' && *pi!=')'||
               top=='{' && *pi!='}'||
               top=='[' && *pi!=']')
               {
                StackDestroy(&st);
                return false;
               }
            //匹配成功弹出栈顶
            StackPop(&st);
        }
        pi++;
    }
    //遍历结束,栈空代表全部闭合
    bool ret =StackEmpty(&st)?true:false;
    StackDestroy(&st);
    return ret;
}
3. main测试调用示例
c 复制代码
int main()
{
    char s1[] = "()[]{}";
    char s2[] = "(]";
    char s3[] = "]";
    printf("%d\n", isValid(s1)); // 输出1 true
    printf("%d\n", isValid(s2)); // 输出0 false
    printf("%d\n", isValid(s3)); // 输出0 false
    return 0;
}

四、三种方案全维度对比表

解题方案 时间复杂度 空间复杂度 核心优点 主要缺点 面试推荐度
暴力循环反复删除 O(n2)O(n^2)O(n2) O(1)O(1)O(1) 不用栈结构,上手门槛低 效率差、代码冗余,不贴合考点
静态数组模拟栈 O(n)O(n)O(n) O(n)O(n)O(n) 不用动态内存,无内存泄漏 数组长度写死,适配超长字符串受限 ⭐⭐
动态char栈标准写法 O(n)O(n)O(n) O(n)O(n)O(n) 适配任意长度,贴合栈考点,工程规范 需要完整封装栈接口 ⭐⭐⭐⭐⭐

五、刷题总结&高频考点梳理

1. 核心考点

栈后进先出特性、动态内存栈封装、分支内存管理、边界条件判断。

2. 记忆口诀

左括号入栈存起来,右括号负责挨个查;

右括号遇空直接错,括号不匹配也错;

字符串走完栈不空,剩余左括号依然错;

所有出口销毁栈,杜绝内存泄漏。

3. 题型复用

栈匹配套路通用所有嵌套闭合题型:HTML标签闭合、多层括号运算、嵌套符号校验。

4. 面试应答要点

面试官重点考察栈LIFO逻辑:括号必须内层先闭合,栈天然满足;同时会检查内存管理,必须所有return分支执行StackDestroy;口述"]""["两个边界用例的完整执行流程。

5. 五大必踩坑提醒

  1. 栈空访问栈顶触发断言 :右括号开头没有提前StackEmpty拦截,StackTop触发断言崩溃,必须先判空再取栈顶;
  2. 遍历完不校验栈空 :输入"["全部左括号入栈,栈有残留,必须判定非法;
  3. 栈类型未改为char :默认int栈存储字符会存在类型隐患,必须typedef char STDataType
  4. 部分分支忘记销毁栈 :多return出口遗漏StackDestroy,动态数组造成内存泄漏;
  5. 匹配逻辑颠倒:只能用栈顶左括号匹配当前右括号,反向判断极易出现匹配错乱。
相关推荐
冻柠檬飞冰走茶1 小时前
PTA基础编程题目集 7-13 日K蜡烛图(C语言实现)
c语言·开发语言·数据结构·算法
晓梦林1 小时前
[Dest0g3 520迎新赛]EasyPHP-学习笔记
android·笔记·学习
Lsir10110_2 小时前
【力扣hot100】矩阵题通关复盘
算法·leetcode·矩阵
退休倒计时2 小时前
【每日一题】LeetCode 207. 课程表 TypeScript
算法·leetcode·typescript
小黄蚁2 小时前
LVGL学习笔记(一)
笔记·学习
玖玥拾2 小时前
LeetCode 80 删除有序数组中的重复项 II
算法·leetcode
青山木3 小时前
Hot 100 --- 电话号码的字母组合
java·数据结构·算法·leetcode·逻辑回归
懿路向前3 小时前
【HarmonyOS学习笔记】2026-07-26 | @Trace 序列化 __ob_ 前缀陷阱与消息持久化
笔记·学习
hanlin033 小时前
刷题笔记:力扣第206题-反转链表
笔记·leetcode·链表