数据结构之堆栈
因为找工作的原因,发现自己的数据结构和算法太薄弱了,被虐的很惨,遂下定决心开始从头学习数据结构和算法,就在MOOC上找了一门浙江大学开设的数据结构课程,也特地开了博客来记录一下此次学习之旅,也在这里定下一个目标,希望能够坚持到底,奥利给!!
什么是堆栈
首先从一个非常经典的算术表达式求解问题开始堆栈的学习。
5 + 6 / 2 - 3 * 4
如何求解上面的表达式?
1、表达式由两类对象构成:运算数和运算符号
2、不同的运算符号有不同的优先级
一般,我们见到的算术表达式称为中缀表达式,因为运算符号位于两个运算数之间,如 a + b * c - d / e。
为了方便计算机计算,我们一般将中缀表达式转换为后缀表达式:如a b c * + d e / -.
如何将中缀表达式转换为后缀表达式
需要借助堆栈来实现,按照我们计算表达式的流程,我们从左往右扫描表达式,并用堆栈来记录运算符号,当我们遇到一个运算符号小于先前记录的运算符序列的最后一个时(同优先级符号的从左往右优先级递减),这就表明我们应该事先运算前面的子表达式,所以将堆栈里的运算符弹出,直到在堆栈里遇到的运算符小于当前的预算符号。带括号的表达式中,左括号在堆栈外时,优先级为最高,当在堆栈里时,优先级最低。所以在扫描表达式时遇到左括号直接入入栈,遇到右括号后,开始弹出堆栈里的运算符,直到遇到左括号后,左括号弹出,因为括号优先级最低,所以此时运算符堆栈应该会出清。
转换规则:
这样,我们就完成了从中缀表达式到后缀表达式的转换,接下来,我们可以对该后缀表达式进行求值运算。
后缀表达式的求值策略:
1、首先我们知道,后缀表达式已经将预算符号的优先级和预算数进行了一定的排列,所以我们可以重做往右扫描,逐个处理运算符和运算数。
遇到预算数时:压入堆栈存储,达到记住未参与运算数的目的
遇到预算符号时:将栈里的运算数取出和运算符做运算,然后将运算结果入栈存储
直到扫描到表达式的最末端,结束搜索,做完运算后,栈应该是空的。
贴上我自己的实现:
/** 求后缀表达式 ***/ #include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX_SIZE 1000 typedef char ElementType; typedef struct t_stack { ElementType data[MAX_SIZE]; int top; }Stack_t; typedef Stack_t * pStack; typedef struct t_dstack { ElementType StackBuf[MAX_SIZE]; int top1; int top2; }DStack_t; pStack stack_New(void) { pStack NewStack = (pStack)malloc(sizeof(Stack_t)); memset(NewStack->data, 0, sizeof(NewStack->data)); NewStack->top = -1; return NewStack; } /* 堆栈存储运算发符号 */ pStack stack = NULL; char tempResult[100] = {0}; int charNum = 0; int stack_isEmpty(Stack_t *s) { if (s) { if (s->top == -1) return 1; else return 0; } else return 1; } ElementType pop(pStack s) { if (!stack_isEmpty(s)) { return s->data[(s->top)--]; } } void push(pStack s, ElementType x) { if (s && s->top != MAX_SIZE - 1) { s->data[++(s->top)] = x; } } ElementType stack_gettop(pStack s) { if (!stack_isEmpty(s)) { return s->data[(s->top)]; } else { return -1; } } int tokenLevel(char token) { int level = 0; if (token == '*' || token == '/') { level = 1; } else if (token == '(') { level = -1; } return level; } int tokenCmp(char checktoken) { int ret = 0; char toptoken = stack_gettop(stack); int checklevel = tokenLevel(checktoken); int stacklevel = tokenLevel(toptoken); if (checktoken == ')' && toptoken == '(') { pop(stack); return 0; } if (toptoken == -1) { /* 空栈直接压入操作符号 */ push(stack, checktoken); return 0; } else if (checklevel <= stacklevel || checktoken == ')') { /* 弹出堆栈内的运算符 */ tempResult[charNum++] = pop(stack); tokenCmp(checktoken); } else { push(stack, checktoken); return 0; } return ret; } int main(void) { char expressString [100] = {0}; stack = stack_New(); int ret = 0; while (scanf("%s", expressString) != EOF) { int i = 0; memset(tempResult, 0, sizeof(tempResult)); charNum = 0; while (expressString[i] != '\0') { if ((expressString[i]) >= '0' && expressString[i] <= '9') { tempResult[charNum] = expressString[i]; charNum++; } else if (expressString[i] == '(') { push(stack, expressString[i]); } else { if (stack_isEmpty(stack)) { push(stack, expressString[i]); } else { tokenCmp(expressString[i]); } } i++; } while (!stack_isEmpty(stack)) { tempResult[charNum++] = pop(stack); } /* 利用堆栈计算值 */ stack->top = -1; for (i = 0; i < charNum; i++) { if (tempResult[i] >= '0' && tempResult[i]<= '9') push(stack, tempResult[i]-'0'); else { int a = pop(stack); int b = pop(stack); switch (tempResult[i]) { case '+': ret = a + b; push(stack, ret); break; case '-': ret = b - a; push(stack,ret); break; case '*': ret = a * b; push(stack,ret); break; case '/': if (b) ret = b / a; push(stack,ret); break; default: break; } } } printf("%d", ret); memset(expressString, 0, sizeof(expressString)); } }
栈的特性:
栈的其他应用
堆栈的抽象数据类型描述
基于数组的栈实现