笔试题:C语言使用顺序栈判断字符串中的括号有效性

一、背景介绍

通过键盘输入一个包括 '(' 和 ')' 的字符串string ,判断字符串是否有效。要求设计算法实现检查字符串是否有效,有效的字符串需满足以下条件:
A. 左括号必须用相同类型的右括号闭合。
B. 左括号必须以正确的顺序闭合。
C. 每个右括号都有一个对应的相同类型的左括号。

我们可以利用 顺序栈(Sequence Stack)来解决这个问题,因为栈结构可以有效地跟踪括号的匹配关系。顺序栈是一个静态实现的栈数据结构,它通过数组实现栈的各项操作,具有较高的时间和空间效率。

本文将介绍如何使用顺序栈来判断括号是否有效,以下是解决该问题的详细步骤。

二、算法设计

解决括号匹配问题的基本思路如下:

  1. 入栈操作
    • 当遇到左括号 ( 时,将其压入栈中。
  2. 出栈操作
    • 当遇到右括号 ) 时,从栈中弹出栈顶元素。
    • 如果栈为空,表示没有对应的左括号,返回无效。
    • 如果弹出的栈顶元素是左括号 (,继续判断下一个字符。
  3. 结束后检查栈是否为空
    • 如果栈不为空,说明有未匹配的左括号,返回无效。
    • 如果栈为空,表示所有括号均有效,返回有效。

版本:

/**
 * @file name : SeqenceIsStrVaild.c
 * @brief     : 通过键盘输入一个包括 '(' 和 ')' 的字符串string ,判断字符串是否有效。要求设计算法实现检查字符串是否有效,有效的字符串需满足以下条件:
A. 左括号必须用相同类型的右括号闭合。
B. 左括号必须以正确的顺序闭合。
C. 每个右括号都有一个对应的相同类型的左括号。
 * @author    : qrshxc@163.com
 * @date      : 2025/04/26
 * @version   : 1.0
 * @note      : 
 * CopyRight (c)  2025-2026   qrshxc@163.com   All Right Reseverd
 */

三、顺序栈的实现

顺序栈是一种通过数组实现的栈数据结构,通常需要维护三个基本操作:入栈、出栈、检查栈是否为空。

1. 顺序栈的定义

我们首先定义顺序栈的结构体 SeqStack_t,包括栈底指针、栈容量和栈顶指针。

c复制编辑typedef char DataType_t; // 栈数据类型

// 顺序栈结构体定义
typedef struct SequenceStack
{
    DataType_t *bottom;  // 记录顺序栈栈底地址
    unsigned int size;   // 记录顺序栈栈容量大小
    int top;             // 记录顺序栈栈顶元素的下标
} SeqStack_t;

2. 顺序栈的基本操作

顺序栈的基本操作包括:栈创建、入栈、出栈、判断栈是否为空。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// 顺序栈的数据类型
typedef char DataType_t;

// 顺序栈的结构体定义
typedef struct SequenceStack
{
    DataType_t *bottom;  // 记录顺序栈栈底地址
    unsigned int size;   // 记录顺序栈栈容量大小
    int top;             // 记录顺序栈栈顶元素的下标
} SeqStack_t;

/**
 * @name      SeqStack_create
 * @brief     创建顺序栈并对顺序栈进行初始化
 * @param     size 顺序栈的大小
 * @return
 *      @retval    manager 顺序栈的管理结构体
 * @date      2025/04/24
 * @version   1.0
 * @note      Manager->Addr  --->  [Size*sizeof(DataType_t)]
 */
SeqStack_t *SeqStack_create(unsigned size)
{
    // 1. 利用 calloc 为栈的管理结构体分配内存
    SeqStack_t *manager = (SeqStack_t *)calloc(1, sizeof(SeqStack_t));
    // 错误处理:如果内存分配失败
    if (manager == NULL) {
        perror("calloc memory for manager failed!");
        exit(-1); // 程序异常终止
    }

    // 2. 利用 calloc 为栈底(数组)分配内存
    manager->bottom = (DataType_t *)calloc(size, sizeof(DataType_t));
    // 错误处理:如果内存分配失败
    if (manager->bottom == NULL) {
        perror("calloc memory for bottom failed!");
        free(manager);
        exit(-1);
    }

    // 3. 初始化栈的容量和栈顶指针
    manager->size = size;
    manager->top = -1; // 栈顶指针初始化为 -1,表示栈为空

    return manager;
}

/**
 * @name      SeqStack_IsFull
 * @brief     判断顺序栈是否已满
 * @param     manager 顺序栈的管理结构体
 * @return
 *      @retval true 栈已满
 *      @retval false 栈未满
 * @date      2025/04/24
 * @version   1.0
 * @note      判断栈的栈顶是否已经达到了栈的容量
 */
bool SeqStack_IsFull(SeqStack_t *manager)
{
    // 如果栈顶元素的下标等于栈的容量减1,说明栈已满
    return manager->top + 1 == manager->size ? true : false;
}

/**
 * @name      SeqStack_Push
 * @brief     入栈
 * @param     data 插入的元素
 * @return
 *      @retval true 入栈成功
 *      @retval false 入栈失败
 * @date      2025/04/24
 * @version   1.0
 * @note      判断栈是否已满,如果没满则将数据压入栈顶
 */
bool SeqStack_Push(SeqStack_t *manager, DataType_t data)
{
    // 判断顺序栈是否已满
    if (SeqStack_IsFull(manager)) {
        printf("SequenceStack is full!\n");
        return false;
    }
    // 如果栈未满,则将数据压入栈顶
    manager->bottom[++manager->top] = data;
    return true;
}

/**
 * @name      SeqStack_IsEmpty
 * @brief     判断顺序栈是否为空
 * @param     manager 顺序栈的管理结构体
 * @return
 *      @retval true 栈为空
 *      @retval false 栈不为空
 * @date      2025/04/24
 * @version   1.0
 * @note      判断栈的栈顶指针是否为 -1,表示栈为空
 */
bool SeqStack_IsEmpty(SeqStack_t *manager)
{
    return (manager->top == -1) ? true : false;
}

/**
 * @name      SeqStack_Pop
 * @brief     出栈
 * @param     manager 顺序栈的管理结构体
 * @return
 *      @retval temp 出栈的值
 * @date      2025/04/24
 * @version   1.0
 * @note      判断栈是否为空,如果栈不为空,则弹出栈顶元素
 */
DataType_t SeqStack_Pop(SeqStack_t *manager)
{
    DataType_t temp = 0; // 用于存储出栈的元素

    // 判断顺序栈是否为空
    if (SeqStack_IsEmpty(manager)) {
        printf("SequenceStack is empty!\n");
        return -1; // 栈空时返回特殊错误值
    }

    // 弹出栈顶元素并更新栈顶指针
    temp = manager->bottom[manager->top--];
    return temp;
}

/**
 * @name      SeqStack_IsStrVaild
 * @brief     判断字符串中的括号是否有效,支持小括号 '(' 和 ')'
 * @param     manager 顺序栈的管理结构体
 * @param     str 待检查的字符串
 * @return
 *      @retval true 字符串有效
 *      @retval false 字符串无效
 * @date      2025/04/24
 * @version   1.0
 * @note      遍历字符串,遇到左括号压栈,遇到右括号弹栈,判断括号是否匹配
 */
bool SeqStack_IsStrVaild(SeqStack_t *manager, const char *str)
{
    char *pstr = (char *)str; // 备份str地址

    // 遍历字符串中的每个字符
    while (*pstr != '\0') {
        if (*pstr == '(') {  // 如果是左括号 '(', 入栈
            SeqStack_Push(manager, '(');
        } else if (*pstr == ')') {  // 如果是右括号 ')', 出栈
            if (SeqStack_IsEmpty(manager)) {  // 判断栈是否为空
                return false;  // 栈空时,表示没有左括号匹配
            }
            SeqStack_Pop(manager);  // 弹出栈顶元素(左括号)
        }
        pstr++;  // 移动到下一个字符
    }

    // 如果栈不为空,表示有未匹配的左括号
    return SeqStack_IsEmpty(manager);
}

/**
 * @name      SeqStack_Print
 * @brief     从头到尾遍历顺序栈的元素
 * @param     manager 顺序栈的管理结构体
 * @return
 *      @retval    
 * @date      2025/04/24
 * @version   1.0
 * @note      遍历栈中的元素并打印
 */
void SeqStack_Print(SeqStack_t *manager)
{
    for (int i = 0; i <= manager->top; ++i) {
        printf("Stack Element[%d] = %d\n", i, manager->bottom[i]);  // 打印顺序栈单元数据
    }
}

int main(int argc, char const *argv[])
{
    // 创建一个顺序栈,栈大小为5
    SeqStack_t *stack = SeqStack_create(5);

    // 测试用例 1
    const char *str1 = "(())";
    if (SeqStack_IsStrVaild(stack, str1)) {
        printf("字符串 \"%s\" 是有效的。\n", str1);
    } else {
        printf("字符串 \"%s\" 是无效的。\n", str1);
    }

    // 测试用例 2
    const char *str2 = "(()";
    if (SeqStack_IsStrVaild(stack, str2)) {
        printf("字符串 \"%s\" 是有效的。\n", str2);
    } else {
        printf("字符串 \"%s\" 是无效的。\n", str2);
    }

    // 清理栈的内存
    free(stack->bottom); // 释放栈底的内存
    free(stack); // 释放栈的管理结构体内存

    return 0;
}

3. 判断括号是否有效

使用顺序栈来检查括号是否匹配有效,我们在遍历字符串的过程中,遇到左括号时压栈,遇到右括号时弹栈。

c复制编辑// 判断括号匹配是否有效
bool SeqStack_IsStrVaild(SeqStack_t *manager, const char *str)
{
    const char *pstr = str;
    while (*pstr != '\0') {
        if (*pstr == '(') {
            SeqStack_Push(manager, '(');  // 左括号入栈
        } else if (*pstr == ')') {
            if (SeqStack_IsEmpty(manager)) {
                return false;  // 栈空时表示没有左括号匹配
            }
            SeqStack_Pop(manager);  // 弹出栈顶元素(左括号)
        }
        pstr++;
    }

    // 如果栈不为空,表示有未匹配的左括号
    return SeqStack_IsEmpty(manager);
}

四、测试用例

接下来,我们通过几个测试用例来验证我们的实现。

c复制编辑int main()
{
    SeqStack_t *stack = SeqStack_create(100);  // 创建栈,容量为100

    // 测试用例 1
    const char *str1 = "(())";
    if (SeqStack_IsStrVaild(stack, str1)) {
        printf("字符串 \"%s\" 是有效的。\n", str1);
    } else {
        printf("字符串 \"%s\" 是无效的。\n", str1);
    }

    // 测试用例 2
    const char *str2 = "(()";
    if (SeqStack_IsStrVaild(stack, str2)) {
        printf("字符串 \"%s\" 是有效的。\n", str2);
    } else {
        printf("字符串 \"%s\" 是无效的。\n", str2);
    }

  

测试结果:

  • 测试用例 1(()) 输出:字符串 "(())" 是有效的。
  • 测试用例 2(() 输出:字符串 "(()" 是无效的。
字符串 "(())" 是有效的。
字符串 "(()" 是无效的。

五、总结

通过使用顺序栈,我们能够高效地判断一个字符串中的括号是否有效。栈的先进后出(LIFO)特性使得我们能够从栈中弹出并检查每个左括号是否匹配相应的右括号。该方法可以扩展到更复杂的括号类型(如 {}[]),并适用于其他类似的匹配问题。

本文通过顺序栈的实现展示了如何处理括号匹配问题,希望对你有所帮助。

posted @ 2025-04-28 16:45  九思0404  阅读(26)  评论(0)    收藏  举报