【C】栈的应用
栈由于其后进先出(LIFO)的特性,是一种比较重要的数据结构,在日常解决问题过程中应用的也比较多。接下来我们就来看两个应用 -- 括号匹配与进制转换。
目录
1 括号匹配
1) 问题描述
2) 算法分析
3) 代码
2 进制转换
1) 问题描述
2) 算法分析
3) 代码
1 括号匹配
leetcode链接https://leetcode.cn/problems/valid-parentheses/description/
1) 问题描述
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
-
左括号必须用相同类型的右括号闭合。
-
左括号必须以正确的顺序闭合。
-
每个右括号都有一个对应的相同类型的左括号。
示例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);
}
}
括号匹配和进制转换是栈的两个经典应用,当然,栈的应用还有很多,比如:模拟递归栈啊,中缀表达式转后缀表达式啊等等。总之,栈的应用非常广泛,希望大家能够通过以上两个应用来加深对栈的理解。