一、栈应用:
括号匹配:
当读入第1个左括号时,期待下一个出现的右括号与第1个左括号匹配,若读入第2个左括号,则期待下一个出现的右括号与第2个左括号匹配…读入第n个左括号,则期待下一个出现的右括号与第n个左括号匹配,匹配成功后,期待下一个右括号与第n-1个左括号匹配…直到所有的左括号全被匹配完成,处理过程与栈思想吻合,因此算法思想如下:
-
设置一个空栈,顺序读入括号
-
若是左括号,压入栈
-
若是右括号,则与栈顶的左括号匹配,不匹配或栈空则报错,匹配则弹出栈顶元素
-
没有括号匹配,则检查栈是否为空,不空则代表有左括号没被匹配,报错,空则代表匹配成功
代码实现:
bool BracketCheck(char Bracket[], int length)
{
SqStack S;
InitStack(S);
for (i = 0; i < length; i++) {
if (Bracket[i] == '(' || Bracket[i] == '{' || Bracket[i] == '[' ) {
Push(S, Bracket[i]);
} else {
if (Empty(S))
return false;
char TopElem;
Pop(S, TopElem);
if (TopElem == '(' && Bracket[i] != ')' )
return false;
if (TopElem == '[' && Bracket[i] != ']' )
return false;
if (TopElem == '{' && Bracket[i] != '}' )
return false;
}
}
return Empty(S);
}
表达式求值:
表达式有三种表示方法:
中缀表达式 infix expression,表示方法为:操作符在两个操作数中间 (如 a-b )
后缀表达式 postfix expression,表示方法为:操作符在两个操作数之后 (如 ab- )
前缀表达式 prefix expression,表示方法为:操作符在两个操作数之前 (如 -ab )
1.中缀表达式转后缀表达式:
中缀表达式转后缀表达式应用’左优先’原则,即不破坏正确性的情况下优先运算左侧的运算符
如图所示最左侧运算 ‘+’ 不破坏正确性,记为①,接下来运算 ‘-’ 会破坏正确性不运算, 向右遍历遇到 ‘*’ 记为②, 遇到 ‘/’ 记为③, 此时运算之前的’-'合法记为④, 最后 ‘+’ 记为⑤.
算法思想如下:
-
设置一个空栈,顺序读入中缀表达式
-
若是操作数,不入栈, 直接加入后缀表达式
-
若是左括号,压入栈
-
若是右括号,弹出栈内运算符加入后缀表达式 (直到左括号弹出),注: 左括号不加入后缀表达式
-
若是运算符,弹出栈内所有大于等于其优先级的运算符 (若碰见左括号或栈空则停止)加入后缀表达式, 再把当前运算符入栈
-
没有操作数处理,弹出栈内所有运算符加入后缀表达式
2.后缀表达式的计算
后缀表达式的计算方法为从左向右遍历,原则为遇到数跳过,遇到运算符则按照后缀表达式方法运算前两位数并保留在式子里,最后保留下唯一数即为结果
算法思想如下:
- 建立一个栈,顺序读入后缀表达式
- 若是操作数,压入栈
- 若是运算符,弹出栈顶两个数进行运算(后弹数 运算符 先弹数)并将结果压入栈
- 没有运算符处理,栈内只剩唯一数为结果
3.用栈实现中缀表达式的计算
用栈实现中缀表达式的计算可结合上述两种算法思想,建立两个空栈,一个用于将中缀表达式转换为后缀表达式,另一个接收后缀表达式并运算
代码实现:
#define DEFAULT 'a'
bool Calc_for_onestep(SqStack &Calc, char operator)
{
if(operator == '(')
return false;
int a, b;
Pop(Calc, b);
Pop(Calc, a);
if (operator = '-')
Push(Calc, a - b);
else if (operator = '+')
Push(Calc, a + b);
else if (operator = '*')
Push(Calc, a * b);
else
Push(Calc, a / b);
return true;
}
bool Calc_InfixExp(char Infix[], int length)
{
SqStack_Trans Trans; //data char 用来存放符
SqStack_Calc Calc; //data int 用来存放数
char operator = DEFAULT; //弹出的符
int result;
for (int i = 0; i < length; i++) {
if (Infix[i] >= '0' && Infix[i] <= '9') {
push(Calc, (int)Infix[i] - 48); //char根据ASCII转成int存入数栈
} else if (Infix[i] == '(') {
push(Calc, Infix[i]);
} else if (Infix[i] == ')') {
if(!Pop(Trans, operator)) //空栈
return false;
for (int j = 0; operator != '('; j++) {
if(!Pop(Trans, operator))
return false; //弹空了也没碰见左括号
if(Calc->Top < 1)
return false; //操作数栈内不足两个数
Calc_for_onestep(Calc, operator);
}
operator = DEFAULT;
} else if (Infix[i] == '+'|| Infix[i] == '-') {
while (!Empty(Trans) && operator != '(') {
Pop(Trans, operator);
if(Calc->Top < 1)
return false;
Calc_for_onestep(Calc, operator); //弹出一个操作符就要对数栈进行一次计算
}
Push(Trans, operator);
} else if (Infix[i] == '*'|| Infix[i] == '/') {
while (!Empty(Trans)&& operator != '('&& GetTop(Trans) != '+'&& GetTop(Trans) != '-') {
Pop(Trans, operator);
if(Calc->Top < 1)
return false;
Calc_for_onestep(Calc, operator);
}
Push(Trans, operator);
operator = DEFAULT;
} else
return false;
}
while (!Empty(Trans)&& GetTop(Trans) != '(') {
Pop(Trans, operator);
if(Calc->Top < 1)
return false;
Calc_for_onestep(Calc, operator);
}
if (GetTop(Trans) == '(')
return false;
Pop(Calc, result);
return true;
}
前缀表达式类似,不再赘述
递归
递归调用时,函数调用栈称为"递归工作栈",每进入一层递归,就将递归调用所需的信息压入栈,每退出一层递归,就从栈顶弹出相应信息.因此递归调用过深时,会造成栈溢出.
将递归算法转换为非递归算法,通常需要借助栈来实现
二、队列应用:
树的层次遍历
在树的层次遍历中,通常建立一个队列,并按照以下规则进行遍历
-
根结点入队,作为队头结点
-
当前队头结点出队,并对队头结点进行访问,其若有左孩子结点则左孩子结点入队,若有右孩子结点则右孩子结点入队,没有则不入队,循环执行直到队空,遍历结束