前面已经介绍了栈的顺序存储和链式存储,其中顺序存储采取了静态存储和动态存储两种方式,下面就具体落实到栈在实际问题中的应用!
1.进制转换问题
这里的进制转换一般是将十进制数转换为一连串的二进制序列的方式。我们由基本的进制转换知识可知,将一个十进制转化为二进制的方法:(辗转相除法)
- 将这个十进制数不断地除以2,然后得到余数。
- 直到被除数为0,即停止,这时得到了一连串的二进制数据。
- 注意:这个二进制序列是该十进制数的倒序,因此要从下往上读取,组成一个二进制数,即为该十进制数的二进制表示形式。
为什么要用栈表示?思路是什么?
因为栈满足先进后出,因此可以将每次得到的二进制数存入栈中,由前面可知,得到的二进制数是倒序的,因此正好通过栈这个特殊的结构实现倒序输出,从而得到该十进制数的二进制表示。
实现方法代码:
//方法的声明
void ConvertBinary(sqStack & S, int Number, int mod);
//方法的实现代码
void ConvertBinary(sqStack & S, int Number, int mod) {
while (Number != 0) {
push(S, Number%mod); //将余数入栈
Number = Number / mod; //让被除数变小
}
}
2.括号匹配问题
所谓括号匹配问题,就是我们输入一连串的括号,比如
[({})]
看它们是否匹配,具体的规则是只要遇到左括号,均直接入栈,然后只要遇到右括号,就开始和栈顶元素进行比较。
括号匹配情况:
左括号和右括号严格数量一致! 左括号数量=右括号数量,且严格匹配
括号不匹配情况:
- 如果只要在比较的过程中,存在一个右括号和左边不匹配的情况,就视为匹配失败。 只要有一个左括号和右括号不匹配,则不匹配
- 如果右边匹配完后,栈不为空(即仍存在多余的左括号)仍视作失败。 左括号数量>右括号数量
- 如果右边有多的,而此时栈为空,仍然匹配失败。 右括号数量>左括号数量
核心函数实现代码:
//函数声明
Status match(sqStack & S,char * p);
//函数实现
Status match(sqStack & S, char * p) {
char e = '\0';
int i = 0;
int flag = 1; //初始化标志,0为未匹配状态,1为匹配状态,初始为匹配状态
while (p[i] != '\0') {
e = '\0'; //每次匹配成功后,都将e置为空字符,也表示如果右括号比左括号多,那么栈顶下一个元素代表空字符
switch (p[i]) {
case '(':
push(S, p[i]);
break;
case '{':
push(S, p[i]);
break;
case '[':
push(S, p[i]);
break;
case ')': //从这里开始就要开始将进栈的括号和栈顶括号开始匹配,如果不匹配直接结束while循环,返回0
pop(S, e);
if ( e!= '(') {
flag = 0;
return flag;
}
break;
case '}':
pop(S, e);
if (e != '{') {
flag = 0;
return flag;
}
break;
case ']':
pop(S, e);
if (e != '[') {
flag = 0;
return flag;
}
break;
default:
break;
}
//能走到这里,表明一次匹配成功,因此让i继续递增,继续比较后面的元素
i++;
}
//这里表示全部循环完且右边的和左边完全匹配,但是可能左边有三个,右边只有两个的情况,因此要继续判断栈是否为空
if (StackEmpty(S)) {
//如果此时栈为空,则表示完全匹配成功!
return flag;
}
else {
return !flag;
}
}
说明:这里的核心处理细节就是e的赋值操作
因为e的值在每次匹配完成后,都保存的是上一个匹配成功的左括号值,当左边全部匹配完后,若右边仍存在括号进行匹配,这时我们进行出栈操作时,并没有元素出栈,但是e保存的仍然是上一个匹配成功的括号,因此我们要对其进行初始化,让其每次匹配成功后,都为空字符,避免对下一次匹配产生干扰。
简而言之:比如我们的栈中数据是
(()))
,此时前四个匹配成功,当匹配最后一个)
时,此时e的值为(
,如果不将它的值置为\0
空字符的话,就会对最后一个匹配产生影响,因此要将每次匹配成功后的那个e置为空字符
完整实现代码:
/**
author:Calarqiang
date:2020-8-19
message:栈的应用之括号匹配
*/
#include<stdio.h>
#define True 1
#define False 0
#define Ok 1
#define Error 0
#define Maxsize 20
typedef char ElementType; //这里要更改一下,因为它里面存放的是字符
typedef int Status;
typedef struct stack {
ElementType data[Maxsize]; //静态分配数组的长度
int top; //栈顶指针
}sqStack;
/*
栈的各种操作
*/
//初始化一个空栈
void InitStack(sqStack & S);
//栈判空
Status StackEmpty(sqStack S);
//进栈
Status push(sqStack & S, ElementType x);
//出栈
Status pop(sqStack & S, ElementType & x);
//读取栈顶元素,并返回该值
Status GetTop(sqStack S, ElementType & x);
//栈的应用
//括号匹配问题
Status match(sqStack & S, char * p);
int main() {
char x; //用于辅助获取栈顶元素
sqStack S;
char str[10] = "\0"; //字符串数组初始化
InitStack(S); //初始化栈
printf("请输入表达式\n");
gets_s(str);
int flag=match(S, str);
if (flag == 1) {
printf("括号匹配成功!\n");
}
else {
printf("括号匹配失败\n");
}
return 0;
}
//初始化一个空栈
void InitStack(sqStack & S) {
S.top = -1; //将栈顶指针初始化为-1,使其始终指向栈顶元素
}
//栈判空
Status StackEmpty(sqStack S) {
if (S.top == -1) {
return True;
}
else {
return False;
}
}
//进栈
Status push(sqStack & S, ElementType x) {
//首先要判断栈是否满了,即可不可能发生溢出
if (S.top == Maxsize - 1) {
printf("栈满了,无法进行入栈操作\n");
return False;
}
else {
S.data[++S.top] = x; //这里相当于两句,即先将栈顶指针+1,然后再进行栈顶元素赋值操作
return True;
}
}
//出栈
Status pop(sqStack & S, ElementType & x) {
//首先要判断栈是否为空
if (StackEmpty(S)) {
printf("栈为空,无法进行出栈操作\n");
return False;
}
else {
x = S.data[S.top--]; //先将值取出,然后栈顶指针进行-1操作。
return True;
}
}
//读取栈顶元素,并返回该值
Status GetTop(sqStack S, ElementType & x) {
if (StackEmpty(S)) {
printf("栈为空,无法获取栈顶元素\n");
return False;
}
else {
x = S.data[S.top]; //将栈顶元素的值赋值给变量x
return True;
}
};
Status match(sqStack & S, char * p) {
char e = '\0';
int i = 0;
int flag = 1; //初始化标志,0为未匹配状态,1为匹配状态,初始为匹配状态
while (p[i] != '\0') {
e = '\0'; //每次匹配成功后,都将e置为空字符,也表示如果右括号比左括号多,那么栈顶下一个元素代表空字符
switch (p[i]) {
case '(':
push(S, p[i]);
break;
case '{':
push(S, p[i]);
break;
case '[':
push(S, p[i]);
break;
case ')': //从这里开始就要开始将进栈的括号和栈顶括号开始匹配,如果不匹配直接结束while循环,返回0
pop(S, e);
if ( e!= '(') {
flag = 0;
return flag;
}
break;
case '}':
pop(S, e);
if (e != '{') {
flag = 0;
return flag;
}
break;
case ']':
pop(S, e);
if (e != '[') {
flag = 0;
return flag;
}
break;
default:
break;
}
//能走到这里,表明一次匹配成功,因此让i继续递增,继续比较后面的元素
i++;
}
//这里表示全部循环完且右边的和左边完全匹配,但是可能左边有三个,右边只有两个的情况,因此要继续判断栈是否为空
if (StackEmpty(S)) {
//如果此时栈为空,则表示完全匹配成功!
return flag;
}
else {
return !flag;
}
}
上面采用的均是栈的静态分配,动态分配和链栈同理!下一次继续讨论栈的其它应用!