栈和队列的应用

1.栈常见应用

1.1 括号匹配

问题描述:假设表达有两种符号:圆的和方的,嵌套的顺序任意,判断嵌套是否正确,如 ([]()) 或 [[()]]均为正确,而 [(]) 或 (()] 均为不正确。

算法描述:

(1)初始化一个空栈,顺序读入括号;

(2)若是左括号直接进栈;

(3)若是右括号,先出栈一个元素比较是否与其匹配,如匹配则继续比较下一个括号,若不匹配则返回匹配失败;

(4)最后若栈不为空,说明还有没匹配的括号,匹配失败,否则匹配成功。

/**
 * 检查括号是否匹配
 * @param match 待匹配字符串
 * @return true 匹配成功,false 匹配失败
 */
public static boolean isMatching(String match){
    char[] braces = match.trim().toCharArray();
    for(char ch : braces){
        if(ch == ‘[‘ || ch == ‘(‘){
            stack.push(ch);
        } else if(ch == ‘]‘){
            char t = stack.pop();
            if(‘[‘ == t){
                continue;
            } else {
                return false;
            }
        } else if(ch == ‘)‘){
            char c = stack.pop();
            if(‘(‘ == c){
                continue;
            } else {
                return false;
            }
        }
    }
    if(stack.size() > 0){
        return false;
    } else {
        return true;
    }
}

1.2 表达式求值

算术表达式由括号、运算符和操作数组成,表达式求值的关键在于如何解析字符串,并按照正确的顺序完成运算。表达式有中缀表达式,不仅要考虑运算符优先级,还要考虑括号,后缀表达式已经考虑了优先级,只有操作数和运算符,在此提供两种方案解决表达式求值。

首先定义运算符优先级,运算符只存在两个位置,栈内和栈外,优先级也分为栈内优先级(in stack priority)和栈外优先级(in comming priority),栈的特性是后进先出,栈外的运算符优先级大,则入栈它迫切先计算,对于相同级别的运算符,栈内的优先级大于栈外的优先级,则出栈。据此设计运算符优先级表:(用isp,icp分别表示栈内和栈外优先级,#号表示字符串结束)

表1 运算符优先级表


操作符


#


(


+,-


*,/


)


isp


0


1


3


5


6


icp


0


6


2


4


1

在Java中使用两个HashMap存储栈内和栈外运算符优先级,代码如下:

static ArrayStack<String> ops = new ArrayStack<String>(10); // 运算符栈
static ArrayStack<Double> vals = new ArrayStack<Double>(20); // 操作数栈

static String opstr = "(+-*/)"; // 涉及到的运算符

static Map<String, Integer> isp = new HashMap<String, Integer>(); // 栈内优先级
static Map<String, Integer> icp = new HashMap<String, Integer>(); // 栈外优先级
static{
    isp.put("#", 0);isp.put("(", 1); isp.put("+", 3); isp.put("-", 3); isp.put("*", 5); isp.put("/", 5); isp.put(")", 6);
    icp.put("#", 0);icp.put("(", 6); icp.put("+", 2); icp.put("-", 2); icp.put("*", 4); icp.put("/", 4); icp.put(")", 1);
}

1.2.1 直接使用中缀表达式求值

算法描述:

(1)初始化两个栈,操作数栈和运算符栈,顺序读取字符串

(2)如果是操作数直接入操作数栈;

(3)如果是运算符,如果当前运算符优先级大于运算符栈顶优先级,则直接入运算符栈,如果小于则循环弹出一个操作符,两个操作数计算,并将结果压入操作数 栈,直到找到运算符优先级比它小的,最后如果当前运算符是右括号,则弹出一个运算符(弹出的是左括号),否则入运算符栈;

(4)如果字符为#说明已经到表达式末尾,则循环弹出操作符栈中的运算符及操作数计算,直到运算符栈为空,最后操作数栈中的元素即为结果。

/**
 * 中缀表达式计算
 */
public static double infix(String expr){
    ops.push("#");
    char[] chs = expr.trim().toCharArray();
    for(char ch : chs) {
        String op = String.valueOf(ch);
        // 如果是# 表示表达式结尾,把栈中的运算符全部弹出
        if("#".equals(op)){
            while(ops.size() > 0 && !"#".equals(ops.peek())){
                vals.push(calculate(vals.pop(), vals.pop(), ops.pop().charAt(0)));
            }
        } else if(!opstr.contains(op)){// 如果是操作数,直接输出
            vals.push(Double.valueOf(op));
        } else { // 运算符
            // 当前运算符大于栈顶运算符优先级,则直接入操作符栈
            if(icp.get(op) > isp.get(ops.peek())){
                ops.push(op);
            } else {
                // 如果小于,循环出栈找到比它优先级小的,弹出一个操作符,两个操作数,计算结果置入操作数栈
                // 如果是 右括号) 即是匹配栈内的左括号),最后弹出),(不入栈
                // 如果不是右括号,最后把这个运算符压入运算符栈
                while(icp.get(op) < isp.get(ops.peek())){
                    vals.push(calculate(vals.pop(), vals.pop(), ops.pop().charAt(0)));
                }

                if(!")".equals(op)){
                    ops.push(op);
                } else {
                    ops.pop(); //弹出  ( 左括号
                }
            }
        }
    }
    return vals.pop();
}

1.2.2 使用后缀表达式求值

后缀表达式已经包含了操作数以及运算符优先级,计算比较方便,遇操作数直接入栈,遇运算符弹出两个操作数计算后再入栈,如此循环,最后操作数栈内即为结果。关键在于如何把中缀表达式转为后缀表达式。

   中缀表达式转后缀表达式,算法描述:

(1)初始化一个运算符栈,顺序读取表达式字符串

(2)如果字符是操作数直接输出

(3)如果字符是运算符,如果当前运算符优先级大于运算符栈顶优先级,则直接入运算符栈,如果小于则循环弹出一个操作符,直到找到运算符优先级比它小的,最 后如果当前运算符是右括号,则弹出一个运算符(弹出的是左括号),否则入运算符栈;

(4)若字符为# 表示已经到末尾,循环弹出运算符栈中的元素,直到栈空,最后的输出即为中缀表达式。

/**
 * 中缀表达式转后缀
 * @param expr
 */
public static String infix2suffix(String expr){
    ops.push("#");
    StringBuffer sb = new StringBuffer();
    char[] chs = expr.trim().toCharArray();
    for(char ch : chs){
        String op = String.valueOf(ch);

        // 如果是# 表示表达式结尾,把栈中的运算符全部弹出
        if("#".equals(op)){
            while(ops.size() > 0 && !"#".equals(ops.peek())){
                sb.append(ops.pop());
            }
        } else if(!opstr.contains(op)){// 如果是操作数,直接输出
            sb.append(op);
        } else { // 运算符
            // 如果当前运算符优先级大于栈顶运算符优先级,则入栈
            if(icp.get(op) > isp.get(ops.peek())) {
                ops.push(op);
                continue;
            }
            // 如果小于,弹出一个操作符,循环出栈直到比它优先级小
            // 如果是 右括号) 即是匹配栈内的左括号),最后弹出),(不入栈
            // 如果不是右括号,最后把这个运算符压入运算符栈
            while(icp.get(op) < isp.get(ops.peek())){
                sb.append(ops.pop());
            }

            if(!")".equals(op)){
                ops.push(op);
            } else {
                ops.pop(); //弹出  ( 左括号
            }
        }
    }
    return sb.toString();
}

1.2.3 递归

所谓递归,就是函数调用了自己,以斐波那契数列为例,其定义为:

这是一个典型的递归例子,Java程序实现如下:

public static int Fib(int n){
    if(n == 0){
        return 0;
    } else if(n == 1){
        return 1;
    } else {
        return Fib(n-1) + Fib(n-2);
    }
}

递归的效率不高,由于其中重复包含很多重复的计算。每个线程执行时,都有各自的栈,我们在debug时可以看到,在递归调用时,递归期间的数据都是保存在这个栈中,系统帮我们维护这个栈。

通常情况下将递归程序转非递归程序,使用自定义栈模拟递归过程,几乎适用于所有递归,站在系统的角度,栈能解决所有问题。但是具体问题,也有其他方法转为非递归,如斐波那契数列可使用直接迭代计算出结果。

/**
 * 非递归实现,不借助栈,直接迭代
 * @return
 */
public static int NotRecur(int n){
    if(n == 0){
        return 0;
    } else if(n == 1){
        return 1;
    } else {
        int n0 = 0,
            n1 = 1; // n 为0,1时的初值
        while(n >= 2){
            int tmp = n1 + n0;
            n0 = n1;
            n1 = tmp;
            n--;
        }
        return n1;
    }
}

后续有些算法,如二叉树的遍历,会分为递归实现和非递归实现。

2.队列常见应用

2.1 层次遍历

在信息处理时,有一类问题需要逐层或逐行遍历,比如二叉树的层次遍历,图的广度优先搜索等。

2.2 缓冲区

缓冲区,是为了匹配速度,主要解决主机和外设速度不匹配,解决多用户引起资源竞争问题。

时间: 2024-10-05 23:30:18

栈和队列的应用的相关文章

快速记忆数组栈和队列函数push()和shift()

在js中,对数组的操作是比较常见的,有时候,我们需要模拟栈和队列的特性才能实现需求,今天来给大家用通俗易懂.简洁明了的几行文字,来告诉大家栈和队列的几个函数,如何快速记住. 首先,概念还是要知道的: 栈(stack)又名堆栈,它是一种运算受限的线性表.其限制是仅允许在表的一端进行插入和删除运算.这一端被称为栈顶,相对地,把另一端称为栈底.向一个栈插入新元素又称作进栈.入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素:从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻

3-3-行编辑程序-栈和队列-第3章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第3章  栈和队列 - 行编辑程序 ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? SequenceStack.c        相关测试数据下载  链接? 无数据

用栈实现队列的效果

用两个栈实现队列的效果,可以入栈,出栈,判空等... 实现的栈基本功能 lstack.h #ifndef _LSTACK_H #define _LSTACK_H #include <stdexcept> using namespace std; // 基于链式表的堆栈 class Stack { public: // 构造过程中初始化为空堆栈 Stack (void) : m_top (NULL) {} // 析构过程中销毁剩余的节点 ~Stack (void) { for (Node* ne

javascript中的栈、队列。

                       javascript中的栈.队列 栈方法     栈是一种LIFO(后进先出)的数据结构,在js中实现只需用到2个函数 push()  接受参数并将其放置数组尾,并返回修改后的数组长度. pop()  移除数组尾的最后一项,并返回移除项的值. 事例: var colors = new Array();var count = colors.push("red","green"); count = colors.push(&

数据结构之栈和队列

数据结构学习继续向前推进,之前对线性表进行了学习,现在我们进入栈和队列的学习.同样我们先学习一些基本概念以及堆栈的ADT. 栈和队列是两种中重要的线性结构.从数据结构角度看,栈和队列也是线性表,只不过是受限的线性表.因此可以称为限定性数据结构.但从数据类型来看,他们是和线性表大不相同的两类重要的抽象数据类型. 栈:(stack)是限定仅在表尾进行相应插入和删除操作的线性表.因此,对栈来说,表尾有其特殊含义,称为栈顶,表头称为栈底,不含元素的空表称为空栈.栈一个重要特性就是后进先出.OK,我们来看

图解堆算法、链表、栈与队列(Mark)

原文地址: 图解堆算法.链表.栈与队列(多图预警) 堆(heap),是一类特殊的数据结构的统称.它通常被看作一棵树的数组对象.在队列中,调度程序反复提取队列中的第一个作业并运行,因为实际情况中某些时间较短的任务却可能需要等待很长时间才能开始执行,或者某些不短小.但很重要的作业,同样应当拥有优先权.而堆就是为了解决此类问题而设计的数据结构.--

第三章:1.栈和队列 -- 栈的表示及实现

前言: 栈和队列 是两种重要的线性结构.从数据结构角度来看,栈和队列也是线性表,它的特殊性在于其操作是线性表的子集,是操作受限的线性表,因此可以称作限定性的数据结构. (限定性:如.人为的规定线性表只能从表尾插入和删除结点数据元素,那么这样的线性表就是栈) 目录: 1.栈 2.栈的应用举例 3.栈与递归的实现 4.队列 5.离散事件模型 正文: 栈的定义 栈(stack) 如上所说,就是限定只能在表尾进行插入和删除的线性表.表尾 称为 栈顶(top), 表头 称为 栈底 (bottom),没有数

数据结构和算法分析(9)表栈和队列的实际应用(一)

    在接下来的几篇博文中,将介绍表.栈.队列在编程实践中的应用.     (1)表达式求值:     输入一个中缀表达式,操作符包括(+ - * / ^).转化为后缀表达式之后并计算表达式的值: 要求: 1.输入的中缀表达式必须是一个完整的字符串: 2.不限制数字的位数和正负,负数用()括起来: 代码如下: 1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 5 #define EmptyT

用两个栈实现队列-剑指Offer

用两个栈实现队列 题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 思路 一个栈的顺序是“后进先出”,再用一个栈把顺序颠倒过来就满足队列的“先进先出”规则了 用两个stack实现,stack1负责进队列,然后从stack2弹栈出队列,若stack2为空,再从stack1弹出到stack2 代码 import java.util.Stack; public class Solution { Stack<Integer> stack1 = new St

(hdu step 8.1.1)ACboy needs your help again!(STL中栈和队列的基本使用)

题目: ACboy needs your help again! Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 73 Accepted Submission(s): 57   Problem Description ACboy was kidnapped!! he miss his mother very much and is very