1.栈(Stack)
定义:栈是一种特殊的线性表,仅允许在固定的一端进行插入和删除操作,进行插入和删除操作的一段称为栈顶,另一端称为栈底,遵循先进后出原则。
栈的底层:栈的底层是一个数组
我们通过idea去看stack这个类会发现它的无参构造函数是空的,但他继承了vector,vector类里面是数组
1.2栈的使用
方法 效果
stack() 构造一个空的栈
push() 往栈里存数据
pop() 弹出栈顶数据(也就是删数据)
peek() 获取栈顶元素但不删除栈顶元素
empty() 判断栈是否为空
size() 获取栈里面数据的个数
1.3栈的模拟实现
说明:栈的底层是一个数组,所有我们先定义一个elem数组,定义一个usedSize记录当前栈里有几个数据。
1.往栈里放数据
说明:首先我们得先判断栈有没有满,然后去放。我们通过画图可以知道,usedSize的值就是你下一个要放数据的位置。
2.弹出数据和观察数据(这里俩个方法放一起来看一下俩个的区别)
,
说明:首先判断是不是空,如果不是就进行下面的弹出或者观察。这里重点看一下弹出这个--usedSize,是如何删的。假如我们栈里已经有3个数据了,那么usedSize等于3,--usedSize为2,会在2下标覆盖一个值。 而usedSize-1,就是2下标的元素返回.
3.利用递归将链表倒着day
说明:递归得去找终止条件,当head.next ==null的时候你就去打印当前val,当不等于null的时候就调用它本身,往后走。当等于了null的时候就会打印,然后归着往前打印。
4.利用栈去逆着打印链表
说明:我们要把链表里的节点去存在栈里,这个栈的类型就是 ListNode的, 循环链表往栈里面放,放完后,当栈里不为空的时候,循环着把栈数据一个一个弹出并打印。
5.逆波兰表达式求值
说明:逆波兰表达式又称为后续表达式,是由中序表达式经过计算得到的(这个计算是从左到右,先乘除加括号,后加减加括号,然后把括号里的符号提到包它括号的外边)
此题思路:当我们那到逆波兰表达式的时候,我们先对存它的数组进行遍历,同时判断它是整数还是符号,是整数的话,我们就把它放到栈里不是的话,我们得把栈里的数据弹出,先弹出的放符号右边,后弹出的放符号左边,进行相关计算,再把计算得到的值放入栈里,最后数组遍历完成后,栈弹出的数据,就是逆波兰表达式计算后的结果。
6.括号匹配
说明:先把存放括号的字符串遍历出来,拿出来就是字符型,先把左括号全部放到栈里面,然后去判断,当不是左括号的话,我们需要当下栈是空吗,如果是,就是括号)(的情况,说明不匹配,如果不为空,我们peek一下栈顶元素,看看当前这个括号和它匹配嘛,如果匹配就把栈的这个括号弹出,字符串继续往后遍历。当遍历完字符串的时候,去判断栈是不是空,是空就是匹配的了,不是就是不匹配的。
7.出栈入栈次序匹配 给定俩个序列比如12345,去判断45321,是不是12345的出栈序列
说明:思路俩个序列分别拿数组存,首先我们去遍历我们的第一个序列,把数据存入栈中,当栈中的数据和第二个序列相同的时候(同时栈不为空且第二个栈还没遍历完),我们就需要把栈里的元素弹出,第二个序列往后遍历。当遍历完的时候,如果栈为空,那么我们第二个序列就是第一个序列的出栈顺序。
8.利用栈求一组数据的最小值(比如1,-1,-2,3)
思路:我们定义俩个栈一个普通栈,一个最小栈,当我们第一次放数据的时候(如果普通栈为空),我们俩个栈都放数据,第二次放的时候就需要拿放的元素和最小栈栈顶元素比较,如果放的元素小于等于最小栈的栈顶元素,那么俩边都放,否则就放到普通栈顶。
出栈操作,当俩栈顶元素相同时,就都出栈,不同时,只出普通栈栈顶元素
最小栈栈顶元素就是这组数据的最小值
9.栈,虚拟机栈,栈帧概念区分?
栈是数据结构
虚拟机栈是一块内存
栈帧是方法调用时在虚拟机栈上开辟的内存
2.队列
2.1队列先进先出
队尾进,队头出
队头
| |
| |
| |
| |
队尾
链表可以实现栈
双向链表,不管是从尾巴进头出,还是从头进尾巴出,时间复杂度都O(1)
单向链表,如果记录了last,从尾巴进入的时候时间复杂度为O(1),从尾巴出的时候时间复杂度为O(n)
单向链表,如果从头进,时间复杂度O(1),出的时候时间复杂度O(1);
链表可以实现队列
双向链表,不管是头进还是尾进时间复杂度都是O(1)
单向链表,如果记录了尾巴,从尾巴进,从头出队,时间复杂度是O(1)
单向链表,如果记录了尾巴,从头进队,从尾出队,因为你得知道最后一个节点的前一个节点,所有你出的时候,时间复杂度为O(n)
2.2队列的使用
在java中,Queue是一个接口,底层通过链表实现
相关方法:
注意Queue是一个接口,在实例化时候必须实例化LinkedList对象,因为LinkedList实现了Queue接口。
注意:它也有add方法,add方法与offer区别是,add会报异常,offer不会,比如说满的了话add会报异常,offer不会。
2.3队列的模拟实现
下面是对这个单链表模拟的队列方法的实现
注意:因为我们是拿链表模拟实现的队列,对列先先出,同时我们复杂度得O(1),所有我们入队都是从尾巴入,也就是尾插
1.往队列里添加数据
2.获取当前队列有多少元素
3.出队列
4.peek队列元素
2.4循环队列
这个东西简单来说就是数组首尾相连
下面是一个图来方便理解:
我们定义一个front,一个rear 去指向这个循环数组的0下标
rear代表我们要放入元素位置的下标
因为一开始rear和front都在0开始放元素和出元素(先进先出),等到最后转回来,这个数组又为空了,一开始为空最后还为空,所有我们就有俩个方式代表它满:
1.当rear == 数组.length-1
2.留一个为满法(这里我们写这种的)
我们要假设一下,当我们rear到了6下标还有要放的就有可能从7下标到0下标过0了
所有rear+1去让rear走肯定不行,这里我们就要观察发现(rear+1)%数组.length可以实现
2.41设计循环队列
3.双端队列
简单来说就是,你入队列出队列,俩边都可以
双端队列是Deque
在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用
Deque<Interger> stack = new ArrayDeque<>;双端队列的线性实现
Deque<Interger> stack = new LinkedList<>;双端队列的链式实现
4.面试题
1.用队列实现栈
说明:思路就是利用俩个队列去实现,外面栈的功能。具体细节思路在代码中有标识 .
---------------- 努力想找工作的茶茶