根据之前链式栈的实现方法来实现栈的应用。https://blog.csdn.net/qq_41078889/article/details/103941387
目录
1.进制转换
进制转换的过程中,我们用短除法得到余数,然后最后将余数倒过来写,这个和栈的后进先出的原理一致,所以可以使用栈实现。将每次得到的余数都压入栈中,当除完到最后时,将栈中的元素全部出栈,就会得到转换之后的数字。
typedef struct Node
{
int data;
struct Node* next;
}node;
//链栈的初始化
void InitStack(node** top)
{
if ((*top = (node*)malloc(sizeof(node))) == nullptr)//为头结点开辟一个存储空间
{
return;
}
(*top)->next = nullptr; //将链栈的头结点指针域置为空
}
//判断链栈是否为空
bool StackEmpty(node* top)
{
if (top->next == nullptr)
{
return true;
}
return false;
}
//进栈操作
int PushStack(node* top, int data)
{
node* p = (node*)(malloc(sizeof(node)));
if (p == nullptr)
{
printf("内存分配失败!\n");
}
else
{
p->data = data;
p->next = top->next;
top->next = p;
}
return 1;
}
//出栈操作
int PopStack(node* top)
{
node* p = top->next;
int tmp;
if (p == nullptr)
{
printf("栈为空!\n");
}
{
int val;
top->next = p->next;
val = p->data;
tmp = val;
free(p); //释放p指向的结点
return tmp;
}
}
//取栈顶元素
int GetTop(node* top)
{
node* p = top;
if (StackEmpty(top))
{
printf("栈为空!\n");
}
if (p->next != nullptr)
{
p = p->next;
}
int data = p->data;
return data;
}
//求表长操作
int StackLength(node *top)
{
int count = 0;
node *p = top;
while (p->next != NULL)
{
count++;
p = p->next;
}
return count;
}
//销毁链栈
void DestoryStack(node* top)
{
node *p = top;
node *q;
while (p != nullptr)
{
q = p;
p = p->next;
free(q);
}
}
//打印栈中元素
void StackPrint(node* top)
{
node* p;
if (StackEmpty(top))
{
printf("栈为空!\n");
}
printf("转换后为:");
p = top;
while (p->next != nullptr)
{
p = p->next;
printf("%-3d", p->data);
}
printf("\n");
}
//进制转换
void atoB(node*top, int a, int b )
{
assert(a != 0 && b != 0);
int tmp1 = a % b;
int tmp2 = a / b;
while (tmp2 != 0 || tmp1 != 0)
{
PushStack(top, tmp1);
tmp1 = tmp2 % b;
tmp2 = tmp2 / b;
}
}
int main()
{
node *Ls;
int a = 888;
int b = 2;
InitStack(&Ls);
printf("将%d转换为%d进制\n", a, b);
atoB(Ls, a, b);
StackPrint(Ls);
printf("栈的长度为:%d\n", StackLength(Ls));
DestoryStack(Ls);
return 0;
}
2.括号匹配问题
对于一个括号的序列,例如 {[()]} ,当我们从左向右遍历时,如果要确定右括号所对应的左括号是否存在,就需要将左括号记录下来,并且要做到最先记录的符号的最后被匹配,那么最合适的数据结构就是栈了。
遇到一个左括号,进栈,遇到一个右括号,判断其与栈顶元素是否匹配,如果不匹配,则返回0标识失败,当遇到’ \0’ 时遍历完括号串,这个时候判断栈是否为空,非空返回0,空返回1标识成功。
//判断左右符号是否匹配,匹配返回1,不匹配返回0
int Match(char a, char b)
{
if (a == '(' && b == ')')
return 1;
else if (a == '[' && b == ']')
return 1;
else if (a == '{' && b == '}')
return 1;
else
return 0;
}
//括号匹配
int Matching(node*top,char expre[],int *data)
{
for (int i = 0; expre[i] != '\0'; i++)
{
if (expre[i] == '(' || expre[i] == '[' || expre[i] == '{')//如果是左括号就进栈
PushStack(top, expre[i]);
else
//StackEmpty十分重要,如果缺少当括号字符串缺少左括号时就会崩溃
if (!StackEmpty(top) && Match(GetTop(top,data), expre[i]))
PopStack(top,data);
else
return 0;
}
if (StackEmpty(top))
return 1;
else
return 0;
}
int main()
{
node *Ls;
int data;
InitStack(&Ls);
char expre[] = "{[(]}";
if (Matching(Ls,expre,&data))
printf("match success!");
else
printf("match fail~");
DestoryStack(Ls);
return 0;
}
3.四则运算表达式求值
首先我们先来看一下什么是后缀表达式,对于9+(3-1)*3+10/2,如果用后缀表达式表示就是9 3 1 - 3 * + 10 2 / +。
那么我们可以从上面的式子看出后缀表达式就是所有的符号都要在运算数字的后面出现。
现在我们看一下后缀表达式是如何计算出表达式的正确结果的。
后缀表达式计算的规则:从左到右遍历表达式的每个数字和符号,如果运到的是数字就进栈,运到的是符号就将栈顶的两个数字出栈,进行运算,运算结果入栈,一直到最终的获取结果。
1.首先初始化一个栈,用来对运算的数字进出使用。
2.然后将表达式中的的前三个数入栈(因为都是数字),9 3 1入栈。
3.接下来是“-”号,将栈顶两个元素出栈,1为减数,3为被减数。进行运算3-1=2,然后将2进栈。
4.数字3进栈。
5.后面是“*”,将3和2出栈相乘得6,在进行入栈。
6.6和9出栈,9+6=15,15入栈。
7.接下来是10 2 两个数字进栈。
8.接下来是“/”号,10/2=5,5入栈。
9.最后一个符号为+,5+15=20,入栈。
10.20出栈,栈变为空。
后缀表达式快速的解决了问题,那么后缀表达式是怎样来的???
呃,我们把平时标准的四则运算表达式9+(3-1)*3+10/2叫做中缀表达式,现在的问题就是如何将中缀表达式转化为后缀表达式9 3 1 - 3 * + 10 2 / +。
转化规则:从左到右遍历中缀表达式的数字和符号,如果是数字就输出成为后缀表达式的一部分。如果是符号的话,就判断它与栈顶符号的优先级,是右括号或者优先级低于栈顶符号,则让栈顶元素依次出栈并输出,将当前符号进站,一直到最终的后缀表达式为止。
接下来看具体的转化过程。
1.先初始化一个空栈,用来对符号进行出栈使用。
2.接下来向右遍历,第一个数字是9,输出。将+进栈。
3.第三个字符是“(”,因为它是左括号,还没有进行配对,进栈。
4.数字3输出,-号进栈,数字1输出。
5.后面的符号是),这时候需要和前面的(进行配对,所以栈顶元素依次出栈,到(输出。
6.接下来是数字3输出,下一个符号*的优先级高于栈中符号的优先级,因此不输出,*进栈。
7.之后是符号+,优先级低于栈顶元素,栈中的元素全部输出,将当前的+入栈。
8.数字10输出,/ 入栈。
9.最后一个数字2输出。
10.因为已经到了最后,将栈中的元素全部输出,得到最终的表达式9 3 1 - 3 * + 10 2 / +。
int Priority(char ch) //判断操作符优先级
{
switch (ch)
{
case '(':
return 3;
case '*':
case '/':
return 2;
case '+':
case '-':
return 1;
default:
return 0;
}
}
int midCal(node*num, node* operato,char*str)
{
assert(str != nullptr);
int i = 0;
int tmp = 0;
int num1 = 0;
int num2 = 0;
while (str[i] != '\0')
{
if (str[i] >= '0' && str[i] <= '9') //判断表达式是否为数字,数字直接进栈
{
//将字符转化为数字
tmp = tmp * 10 + str[i] - '0';
i++;
if (str[i] > '9' || str[i] < '0')
{
PushStack(num, tmp); //操作数进栈
tmp = 0;
}
}
else//运算符
{
// 1、操作符出栈不计算
if (GetTop(operato) == '(' && str[i] == ')') //直接出栈,不计算,栈顶为'(' ,表达式为')'
{
PopStack(operato); //括号直接出栈
i++;
continue; //继续下一次循环
}
// 2、操作符进栈
if ((StackEmpty(operato) == true) //操作符栈为空
|| (Priority(str[i]) > Priority(GetTop(operato))) //表达式操作符优先级 > 栈顶操作符优先级
|| (GetTop(operato) == '(' && str[i] != ')')) //栈顶为'(' && 表达式当前值不为 ')'
{
PushStack(operato, str[i]); //操作符进栈
i++;
continue; //继续下一次循环
}
// 2、操作符出栈计算
if (Priority(str[i]) <= Priority(GetTop(operato)) //表达式操作符优先级 <= 栈顶操作符优先级
|| (str[i] == '0' && StackEmpty(operato) != true) //表达式为空 && 操作符栈不为空
|| str[i] == ')') //表达式为')'
{
num1 = GetTop(num); //数字栈顶出栈
num2 = GetTop(num); //数字栈第二个数字出栈
switch (PopStack(operato))
{
case '+':
PushStack(num, num2 + num1);
break;
case '-':
PushStack(num, num2 - num1);
break;
case '*':
PushStack(num, num2 * num1);
break;
case '/':
PushStack(num, num2 / num1);
break;
}
}
}
}
printf("Result = %d\n", GetTop(num));
}