当前位置: 首页 > news >正文

【C】栈的应用

   栈由于其后进先出(LIFO)的特性,是一种比较重要的数据结构,在日常解决问题过程中应用的也比较多。接下来我们就来看两个应用 -- 括号匹配与进制转换。


目录

1  括号匹配

1) 问题描述 

2) 算法分析

3) 代码

2  进制转换

1) 问题描述

2) 算法分析

3) 代码  


1  括号匹配

leetcode链接https://leetcode.cn/problems/valid-parentheses/description/

1) 问题描述 

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

  有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。

  2. 左括号必须以正确的顺序闭合。

  3. 每个右括号都有一个对应的相同类型的左括号。


示例1:

输入:s = "()"

输出:true

示例2:

输入:s = "()[]{}"

输出:true

示例3:

输入:s = "(]"

输出:false

  这个题目的意思是给你一个字符串,字符串里包含'(',')','[',']','{','}' 6种括号字符需要让你判断出所给的括号序列是不是能满足正好匹配上,如:"( [ { ( ) } ] )" 这个序列就是匹配的括号;而"( [ { ] } )" 、"{"、")" 等就不是匹配的括号。


2) 算法分析

  要解决这个问题需要用到一个栈 st,开始我们让一个字符指针 pc 来遍历原字符串,如果 pc 位置是左括号('(' ,'[','{'),那就让该字符入栈如果 pc 位置是右括号,那就取栈顶元素,让栈顶元素与 pc 位置的字符相比较,如果能够匹配,那就让 pc++ 比较下一个字符,如果不匹配,那就直接返回 false以上过程如图所示:

  在这里再考虑一下特殊情况,那就是如果字符串中右括号数量比左括号多、左括号数量比右括号多两种情况。如果是字符串中右括号数量比左括号多的情况,在遇到某个右括号取栈顶元素的时候栈里面会是空的。所以在遇到右括号判断栈顶元素与 pc 位置元素是否匹配时,需要先判断栈是否为空,如果为空,直接返回 false;如果是第二种情况,当 pc 遍历完字符串后,栈中还会有元素,所以在 pc 遍历完字符串后,需要再判断栈是否为空,如果不为空就直接返回 false ,如果为空,返回 true

  有一点需要注意,就是在 return false 和 return true 之前,需要先销毁一下栈,因为栈中的 arr 数字指针指向了开辟的内存空间,如果没有先释放空间再返回的话,会造成内存泄露的问题。另外,也不要忘记将栈给初始化。

  在这里提醒一下,就是之前写过的栈里面存的元素都是 int ,这里需要改成 char 。


3) 代码

typedef char STDataType;
//定义栈的结构--用数组来实现
typedef struct Stack
{
	STDataType* arr;
	int top;//指向栈顶
	int capacity;//容量
}Stack;

//初始化
void StackInit(Stack* ps)
{
	assert(ps);
	ps->arr = NULL;
	ps->capacity = ps->top = 0;
}
//销毁
void StackDestroy(Stack* ps)
{
	assert(ps);
	if (ps->arr)
		free(ps->arr);
	ps->capacity = ps->top = 0;
}
//入栈
void StackPush(Stack* ps, STDataType x)
{
	assert(ps);
	if (ps->top == ps->capacity)
	{
		//增容
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(ps->arr, sizeof(STDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail!\n");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = newCapacity;
	}
	ps->arr[ps->top++] = x;
}
//出栈
void StackPop(Stack* ps)
{
	assert(ps);
	ps->top--;
}
//判空
bool StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->top == 0;
}
//取栈顶元素
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps));

	return ps->arr[ps->top - 1];
}
//有效元素个数
int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}
bool isValid(char* s) {
    char* pc = s;
    Stack st;
    StackInit(&st);
    while (*pc != '\0')
    {
        //如果是左括号,那就入栈
        if (*pc == '(' || *pc == '[' || *pc == '{')
        {
            StackPush(&st, *pc);
        }
        else
        {
            //如果遇到右括号,栈为空,那就是无效的括号
            if (StackEmpty(&st))
            {
                StackDestroy(&st);
                return false;
            }
            else{
                //进行匹配,如果右括号不是对应的左括号,那就是无效的括号
                STDataType ret = StackTop(&st);
                if ((ret == '(' && *pc != ')') || (ret == '[' && *pc != ']') || (ret == '{' && *pc != '}'))
                {
                    StackDestroy(&st);
                    return false;
                }
                StackPop(&st);
            }
        }
        pc++;
    }
    bool ret = StackEmpty(&st) ? true : false;
    StackDestroy(&st);
    return ret; 
}

2  进制转换

1) 问题描述

  进制转换是栈的另一个应用。这个应用主要是实现不同进制的数字之间的相互转换,如十进制转二进制,十进制转八进制等等。这里主要是实现十进制与八进制之间的转换,其余进制转换原理类似。

2) 算法分析

  其实用栈实现进制转换,主要是模拟进制转换的过程,以下是十进制数字 1107 转成八进制数字 2123 的过程:

1107 每次除以8,然后除完的整数部分再除以8,直到整数部分为0为止,每次除以的余数倒取就是八进制数字。

  所以通过这个过程,我们可以看到其先除出来的余数正好是最后取的,正好符号栈后进先出的特性,所以进制转换需要借助数据结构栈。

  首先初始化一个栈 st,让十进制数字 num 循环除以8直到为0,让每次除出来的余数入栈,然后再循环判断栈是否为空,取栈顶元素,出栈就是八进制数字了。


3) 代码 

#include<stdio.h>

//这里定义栈的过程省略了,如果要看的话可以看
//栈的那一篇文章或者括号匹配应用

void Octal_conversion(size_t nums)
{
  Stack st;
  //栈不要忘记初始化
  StackInit(&st);

  while (nums)
  {
    //十进制数除以8的余数进栈
    StackPush(&st, nums % 8);
    //十进制数字除8
    nums /= 8;
  }
 
  //循环取出栈顶元素
  while (!StackEmpty(&st))
  {
    printf("%d ", StackTop(&st));
    StackPop(&st);
  }
}

  括号匹配和进制转换是栈的两个经典应用,当然,栈的应用还有很多,比如:模拟递归栈啊,中缀表达式转后缀表达式啊等等。总之,栈的应用非常广泛,希望大家能够通过以上两个应用来加深对栈的理解。

相关文章:

  • javaweb + AI day 02
  • 用deepseek学大模型08-长短时记忆网络 (LSTM)
  • Linux 新建用户和组命令全解析
  • linux查看程序占用的本地端口
  • 【Hugging Face系列篇】01初步介绍:“AI界的GitHub”
  • Java 基础面试
  • Vue_vue2/vue3无缝滚动效果
  • Discuz! X3.5 根目录权限设置
  • java每日精进 2.13 MySql迁移人大金仓
  • 【Java】泛型与集合篇 —— Set 接口
  • RT-Thread+STM32L475VET6实现红外遥控实验
  • 开题报告——基于Spring Boot的垃圾分类预约回收系统
  • 深度学习pytorch之19种优化算法(optimizer)解析
  • 【算法】动态规划专题⑦ —— 多重背包问题 + 二进制分解优化 python
  • umi react+antd 判断渲染消息提示、input搜索、多选按钮组
  • 【核心算法篇十二】《深入解剖DeepSeek多任务学习:共享表示层的24个设计细节与实战密码 》
  • 数组和指针常见笔试题(深度剖析)
  • Ubuntu:20.04更新cmake到更高版本
  • WebGPU 中的缓冲区输入速率:逐顶点与逐实例模式详解
  • FreeSwitch中mod_dptools和mod_easyroute两个模块及应用场景
  • 咖啡戏剧节举办第五年,上生新所“无店不咖啡,空间皆可戏”
  • 新华时评:任凭风云变幻,中俄关系从容前行
  • 夜读丨母亲的手擀面
  • 再有20余篇论文出现“妇科男患者”“前列腺女患者”,如何破除“水论文”灰产链?
  • 现场|万里云端,遇见上博
  • 暴雨蓝色预警:南方开启较强降雨过程