Q: 栈、队列与数组的区别?

A: 本篇主要涉及三种数据存储类型:栈、队列和优先级队列,它与数组主要有如下三个区别:

A: (一)程序员工具 
数组和其他的结构(栈、队列、链表、树等等)都适用于数据库应用中作为数据记录。它们常用于记录那些对应于现实世界的对象和活动的数据,如职员档案等,这些结构便于数据的访问:它们易于进行插入、删除和查找特定数据项的操作。 
然而,本篇要讲解的数据结构和算法更多的是作为程序员的工具来运用。它们主要作为构思算法的辅助工具,而不是完全的数据存储工具。这些数据结构的生命周期比那些数据库类型的结构要短得多。在程序操作执行期间它们才被创建,通常用它们去执行某项特殊的任务;当完成任务之后,它们则被销毁。

A: (二)受限访问 
在数组中,若知道数据项的下标,便可以立即访问该数据项;而在本篇的数据结构中,访问是受限制的,即在特定时刻只有一个数据项可以被读取或者删除。 
这些结构接口的设计增强了这种受限访问,访问其他数据项理论上是不允许的。

A: (三)更加抽象 
栈、队列和优先队列是比数组和其他数据存储结构更为抽象的结构。主要是通过接口对栈、队列和优先队列进行定义,接口表明了它们可以完成的操作,而主要实现机制对用户来说是不可见的。 
例如:栈的实现机制可以用数组实现,本篇的示例就是这样处理的,但它也可以用链表来实现。优先级队列的内部实现可以用数组或一种特别的树-堆来实现。

Q: 什么是栈?

A: 栈只允许访问一个数据项:即最后插入的数据项。移除这个数据项才能访问倒数第二个插入的数据项,以此类推。

Q: 栈有哪些实际场景?

A: 大部分微处理器运用基于栈的体系结构,当调用一个方法时,把它的返回值和参数压入栈,当方法结束返回时,哪些数据出栈。栈操作就嵌入在微处理器中。

Q: 栈的Java代码?

A: 在本例中,StackX类里面数据项的类型为long型,是用数组来实现,top变量存储栈顶元素的下标。

示例: StackX.java, StackXTest.java

Q: 栈示例1:单词逆序?

A: 栈的第一个例子是做一件非常简单的事情:单词逆序。因为栈的后进先出(LIFO)的特点,所以字母的顺序就颠倒了。

示例:Reverser.java

Q: 栈示例2:分隔符匹配?

A: 栈通常用于匹配左分隔符和右分隔符,因为最后出现的左边分隔符需要最先匹配,这个规律符合栈的LIFO的特点。 
下面是一些例子:

c[d]        // correct
a{b[c]d}e // correct
a{b(c]d}e // not correct; ] doesn’t match (
a[b{c}d]e} // not correct; nothing matches final }
a{b(c) // not correct; nothing matches opening {

分隔符匹配程序从字符串中不断地读取字符,每次读取一个字符。若发现它是左分隔符,将它压入栈中。当读到一个右分隔符时,弹出栈顶的左分隔符,并且查看它是否和右分隔符向匹配。(注:非分隔符的字符不插入栈中,只需忽略它们。)

示例:BracketChecker.java

Q: 栈的效率?

A: StackX类中实现的栈,入栈和出栈的时间复杂度为常数O(1), 栈操作所消耗的时间不依赖于栈中数据项的个数,因此操作时间很短。栈不需要比较和移动操作。

Q: 什么是队列?

A: 队列(Queue)在词典里是“排队”(waiting line)的意思。计算机科学中,队列是一种数据结构,有点类似栈,只是队列中的第一个插入的数据项最先被移除(先进先出,FIFO),而在栈中,最后插入的数据项最先移除(LIFO)。

Q: 什么是循环队列?

A: 为了避免队列不满却不能插入新数据项的问题,可以让队头队尾指针绕过到数组开始的位置,这就是循环队列。

Q: 队列有哪些实际的场景?

A: 使用文字处理器,敲击一个键,而计算机又暂时要做其他的事情。敲击的内容不会被丢失,它会在队列中等待,知道文字处理程序有时间来读取它。利用队列保证了键入内容在处理时其顺序不会改变。

Q: 队列的Java代码?

A: Queue类不但有mFront(队头)和mRear(队尾),还有队列当中当前数据项的个数mSize。

A: add/offer()方法 
将指定的元素插入此队列。区别在于add(e)会抛出异常,offer(e)返回特殊值。

内部调用insert()方法,一般情况,插入操作mRear(队尾指针)加一后,在队尾指针所指的位置处插入新的数据项。但是,当mRear指向数组的最后一个元素,即mItems.length - 1位置的时候,在插入数据项之前,它必须回到数组的第一个元素前面,即mRear设置为-1,因此当mRear加1后,它等于0,是数组第一个元素的下标值。最后,mSize加一。

A: remove()/poll()方法 
获取并移除此队列的头。区别在于remove(e)会抛出异常,poll(e)返回特殊值。

内部调用extract()方法,该方法总是由mFront指针得到队头数据项的值,然后将mFront加一。但是,如果这样做mFront会超过数组的大小,mFront必须绕回到数组下标为0的位置上。注意先将返回值临时存储起来。最后mSize减一。

A: peek()方法 
获取但不移除此队列的头;如果此队列为空,则返回NULL_ELEMENT。

A: 示例:Queue.java

Q: 没有数据项计数字段的队列Java代码?

A: 在Queue类中包含数据项计数字段mSize会使insert()和extract()方法增加一点额外的操作,因为处理insert()和remove()方法必须分别递增和递减这个变量值。这可能算不上额外的开销,但是如果处理大量的插入和移除操作,这就可能会影响性能。因此,一些队列的实现不使用数据项计数的字段,而是通过mFront和mRear来计算出队列是否为空或者满以及数据项的个数。

示例:Queue.java

Q: 队列的效率?

A: 和栈一样,队列中插入数据项和移除数据项的时间复杂度均为O(1)

Q: 双端队列?

A: 

Q: 优先级队列?

A: 优先级队列是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素。

Q: 优先级队列有哪些实际场景?

A: 例如,在抢先式多任务操作系统中,程序排列在优先级队列中,这样优先级最高的程序会得到时间片并得以运行。

Q: 优先级队列的Java代码?

A: 本示例使用有序数组实现优先级队列,这种方式插入比较慢,但是它比较简单,适用于数据量比较小并且不是特别注重插入速度的情况。

示例:PriorityQueue.java

A: 最小关键字值得数据项总是数组的高端(最高下标值处),而最大的数据项在低端(下标值为0的位置)。 

A: 待出队的数据项总是在数组的高端,所以删除操作又快又容易。删除数据项后,队头指针下移指向队列新的高端,不需要移动和比较。

A: 注意没有指针回绕,队尾指针从不移动,它总是指着数组低端的元素。

A: Font和Rear指针是为了和普通队列做比较,实际上并不需要它们。

A: 在数据项个数比较少,或不太关心速度的情况下,用数组实现优先级队列还可以满足要求。如果数据项很多,或速度很重要时,采用堆是更好的选择。

Q: 优先级队列的效率?

A: 本示例的优先级队列插入操作需要O(N)时间,而删除操作则需要O(1)的时间。后面将介绍如何通过堆来改进插入操作的时间。

Q: 如何解析算术表达式?

A: 对于诸如 2*(3+4) 或者 ((2+4)*7)+3*(9-5)的算术表达式,前面我们已经演示了怎么应用栈来检查分隔符是否匹配正确,但是接下来我们将学习如何解析这些算术表达式。

事实上,对计算机算法来说如果要直接求算术表达式的值还是相当困难,因此对于算法来说分这两步实现更容易: 
1. 将算术表达式转换成另一种形式:后缀表达式 
2. 计算后缀表达式的值

Q: 中缀/后缀表达式?

A: 中缀表达式:操作符放在两个操作数之间。比如A+B或A/B等。 
A: 后缀表达式:也称作逆波兰表达式(Reverse Polish Notation或者RPN),它是由一位波兰的数学家发明的。操作符跟在两个操作数的后面,而且不包含括号。这样A+B 就变成AB+,A/B变成AB/。

A: 下面是中缀表达式转化为后缀表达式的例子: 

Q: 人类是如何计算中缀表达式?

A: 由于Mr. Klemmer的长期的教学教育,对于3+4+5或者3*(4+5)表达式,我们人类做起这个问题却相当容易。通过分析人类如何计算这些表达式的值,可以得出转化为后缀表达式的启示: 
粗略地说,解析算术表达式时,应该遵循下列几条规则: 
1. 从左到右读取算式 
2. 已经读到了可以计算值得两个操作数和一个操作符时,就可以计算,并用计算结果代替那两个操作数和哪个操作符 
3.继续这个过程--从左到右,能算就算--直到表达式的结尾。

A: 示例:3 + 4 - 5

 

A: 示例: 3 + 4 * 5 
 

A: 示例: 3 * (4 + 5) 
 

Q: 如何把中缀表达式转换成后缀表达式?

A: 正如前面看到的那样,计算中缀表达式,既要向前,也要向后读表达式。将中缀表达式转换成后缀表达式的规则和计算中缀表达式的规则类似。但是还是有点变化。

A: 将中缀表达式转换成后缀表达式不用做算术运算,它不求中缀表达式的值,只是把操作数和操作符重新排列成另一种形式。

A: 转换规则: 

A: 示例:A+B-C 

A: 示例:A+B*C 

A: 示例:A*(B+C) 

Q: 中缀表达式转换成后缀表达式的Java代码 ?

A: 示例: In2PostTransform.java

Q: 人类如何用后缀表达式求值?

A: 下图展示了人类是如何通过观察和铅笔在纸上计算后缀表达式的。 
示例:345+*612+/– 

从左边第一个操作符开始,把它和它左边相邻的两个操作数画在一个圆圈里,然后应用操作符运算两个操作数并把结果写在圆圈里。最大的圆圈里算出来的值就是这个表达式最后的结果。

Q: 后缀表达式求值的规则?

A: 怎么才能写一个程序来重复刚才的求值过程呢?正如上面所描述,每遇到一个操作符,就用它来运算在这之前最后看到的两个操作数,这就表明可以利用栈来存储操作数。它的规则如下: 

算术操作的结果被压入到栈中,在最后一个字符(一定是操作符)被读取并执行计算以后,栈里只剩一个数据项,它就是整个表达式的运算结果。

Q: 后缀表达式求值的Java代码?

A: 为了简化代码,这里限制只能输入一位数的数字。 
示例:PostfixParser.java

Q: 本篇小结?

  • 栈、队列和优先级队列是经常用于简化某些程序操作的数据结构。
  • 在这些数据结构中,只有一个数据项可以被访问。
  • 栈只允许访问最后一个插入的数据项。
  • 队列只允许访问第一个插入的数据项。
  • 队列可以实现为循环队列,它基于数组,数组下标可以从数组末端回绕到数组的开始位置。
  • 优先级队列的重要操作是有序地插入新数据项和移除关键字最小(有时是最大)的数据项。
  • 这些数据结构可以用数组实现,也可以用其他机制(例如链表)来实现。

Q: 参考

    1. 《Java数据结构和算法》Robert Lafore 著,第4章 - 栈和队列

Java数据结构和算法 - 栈和队列的更多相关文章

  1. java数据结构与算法值优先级队列

    一.优先级队列 什么是优先级队列:优先级队列是一种比栈和队列更加常用的一种数据结构.在优先级队列中,数据项按照关键字的值有序.数据项插入到队列中时,会按照顺序插入到合适的位置,用来保证队列的顺序. 生 ...

  2. java数据结构和算法03(队列和优先级队列)

    什么是队列呢?其实队列跟栈很像,我们可以把栈的底部给弄开,这样数据就可以从下面漏出来了,我们就从下面拿就好了. 可以看到队列是新进先出,就跟我们显示生活中的排队一样,买火车票,飞机票等一样,先去的肯定 ...

  3. Java数据结构与算法(4) - ch04队列(Queue和PriorityQ)

    队列: 先进先出(FIFO). 优先级队列: 在优先级队列中,数据项按照关键字的值有序,关键字最小的数据项总在对头,数据项插入的时候会按照顺序插入到合适的位置以确保队列的顺序,从后往前将小于插入项的数 ...

  4. java 数据结构与算法---栈

    原理来自百度百科 一.栈的定义 栈是一种只能在一端进行插入和删除操作的特殊线性表:它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数 ...

  5. 6, java数据结构和算法: 栈的应用, 逆波兰计算器, 中缀表达式--> 后缀表达式

    直接上代码: public class PolandCalculator { //栈的应用:波兰计算器: 即: 输入一个字符串,来计算结果, 比如 1+((2+3)×4)-5 结果为16 public ...

  6. 5, java数据结构和算法: 栈 , 入栈, 出栈, 正序遍历,,逆序遍历

    直接上代码: class ArrayStack{ //用数组模拟栈 int maxSize; int[] stack; int top = -1;//表示栈顶 public ArrayStack(in ...

  7. Java数据结构和算法(五)——队列

    队列.queue,就是现实生活中的排队. 1.简单队列: public class Queqe { private int array[]; private int front; private in ...

  8. Java数据结构和算法(二)--队列

    上一篇文章写了栈的相关知识,而本文会讲一下队列 队列是一种特殊的线性表,在尾部插入(入队Enqueue),从头部删除(出队Dequeue),和栈的特性相反,存取数据特点是:FIFO Java中queu ...

  9. 【Java数据结构学习笔记之二】Java数据结构与算法之栈(Stack)实现

      本篇是java数据结构与算法的第2篇,从本篇开始我们将来了解栈的设计与实现,以下是本篇的相关知识点: 栈的抽象数据类型 顺序栈的设计与实现 链式栈的设计与实现 栈的应用 栈的抽象数据类型   栈是 ...

随机推荐

  1. tkinter中表格的建立(十三)

    表格的建立 import tkinter from tkinter import ttk wuya = tkinter.Tk() wuya.title("wuya") wuya.g ...

  2. vue学习之响应式原理的demo实现

    Vue.js 核心: 1.响应式的数据绑定系统 2.组件系统. 访问器属性 访问器属性是对象中的一种特殊属性,它不能直接在对象中设置,而必须通过 defineProperty() 方法单独定义. va ...

  3. Python爬虫-pyspider框架的使用

      pyspider 是一个用python实现的功能强大的网络爬虫系统,能在浏览器界面上进行脚本的编写,功能的调度和爬取结果的实时查看,后端使用常用的数据库进行爬取结果的存储,还能定时设置任务与任务优 ...

  4. IDEA破解 Intellij IDEA license server 激活(可用)

    激活地址如下图所示: 2018 intellij idea 注册码(亲测可用): C0FHYYCJ22-eyJsaWNlbnNlSWQiOiJDMEZIWVlDSjIyIiwibGljZW5zZWVO ...

  5. java判断一个字符串是否是数字的三种方法

    参考https://blog.csdn.net/ld_flex/article/details/7699161 1 用JAVA自带的函数 public static boolean isNumeric ...

  6. 在Windows Server 2008 R2下搭建jsp环境(二)-mysql的下载安装

    个人推荐java开发过程中的数据库首选MYSQL,MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司.MySQL是一种关联数据库管理系统,关联数据库将数据保 ...

  7. BZOJ_2820_YY的GCD_莫比乌斯反演

    BZOJ_2820_YY的GCD_莫比乌斯反演 题意&分析: 首先f[i]非积性,但可以通过μ处理,所以我们考虑线筛 f[i*p]=μ[i*p/p']; 1.当i为质数时f[i]=1; 2.当 ...

  8. (3)STM32使用HAL库操作外部中断——实战操作

    有了上一篇的基础入门知识,使用Cube创建一个简单的外部中断就容易多了. 一.Cube配置 需求:使用PD10作为外部中断(下降沿触发)控制LED(PD12-PD14) 1.选型 STM32-F4-D ...

  9. 解决 'boost/iterator/iterator_adaptor.hpp' file not found’ 及控制台":CFBundleIdentifier", Does Not Exist

    "react-native": "0.46.1" 这个问题产生原因: * /Users/Vanessa/.rncache 中 boost_1_63_0.tar. ...

  10. 基于Java实现红黑树的基本操作

    首先,在阅读文章之前,我希望读者对二叉树有一定的了解,因为红黑树的本质就是一颗二叉树.所以本篇博客中不在将二叉树的增删查的基本操作了,需要了解的同学可以到我之前写的一篇关于二叉树基本操作的博客:htt ...