《栈与队列:数据结构的"双生花"》
- 一.栈:后进先出
- 这一章的栈和队列比较简单;
- 二.队列:先进先出
- 注意:队列他是接口,接口,接口!
- 三.栈和队列题目
-
- [1. 括号匹配](#1. 括号匹配)
- [2. 逆波兰表达式求值](#2. 逆波兰表达式求值)
- [3. 出栈入栈次序匹配](#3. 出栈入栈次序匹配)
- [4. 最小栈](#4. 最小栈)
- 四.面试题
-
- [1. 用队列实现栈。](#1. 用队列实现栈。)
- [2. 用栈实现队列。](#2. 用栈实现队列。)
一.栈:后进先出
1.1认识栈
这一章的栈和队列比较简单;
首先是:后进先出的栈
我们可以把栈理解为简化版的顺序表,他最主要的就是三个操作:
①入栈------>尾插
②出栈------>尾删
③取栈顶元素

栈的方法:

入栈:push();
出栈:pop();
取栈顶元素:peek();
1.2后进先出
后进先出,这个不难理解,就是后入栈的元素,进行出栈或者取栈顶元素时,先处来,俗话说枪打出头鸟,后来者机会大,这就是后进先出;
代码演示:
java
//实例化栈
Stack<Integer> stack = new Stack<>();
//注意这种引用类型存储的变量要写成包装类型;
//入栈
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
//出栈
int a = stack.pop();//这里是4;
System.out.println(a);//4
//取栈顶元素
System.out.println(stack.peek());//这里是3

1.3基于数组的栈模拟
①.入栈
java
class MyArrayStack{
//首先创集一个数组
private int[] data;
private int size;
public MyArrayStack(int size) {
this.data = new int[size];
}
public MyArrayStack() {
this.data = new int[10];
}
private void grow(){
int[] Edata = new int[2*data.length];
for (int i = 0; i < data.length; i++) {
Edata[i] = data[i];
}
data = Edata;
}
//入栈模拟
public void push(int val){
if(data.length<=size){
grow();
}
data[size] = val;
size++;
}
}
②.出栈
java
public int pop(){
if (size == 0) throw new RuntimeException("栈为空");
//size先减减,扩容减一,然后返回栈顶元素
return data[--size];
}
③.取栈顶元素
java
public int peek(){
if (size == 0) throw new RuntimeException("栈为空");
return data[size-1];
}
二.队列:先进先出
2.1认识队列
队列与栈不同,他是先进先出,也就是先入队列的`元素先出来,后来的慢慢排队,这个在我们日常生活中是很常见的,比如排队吃饭,肯定是先排在前面的先吃到饭,后排的后吃饭;
队列方法:

注意:队列他是接口,接口,接口!

这里我只说链表实现了这个接口,这是最常见,最常用的的向上转型,其他的以后再说;
java
//用链表的向上转型
Queue<Integer> queue = new LinkedList<>();
queue.offer(1);
queue.offer(2);
queue.offer(3);
int b = queue.poll();//1
System.out.println(b);//打印出先进去的1
2.2队列图解
队列我们也是要掌握三种基本操作:入队列,出队列,取队首元素;
入队列-----尾插
出队列-----头删
取队首元素

2.3 以数组模拟队列
这里我们用不一样的方法,之前我们在写入栈方法时,他是属于动态内存,用完了,可以直接继续不断地扩容,这次我们用固定数组
首先创建头标head,尾标tail;
java
public class MyArrayQueue {
public int[] data;
public int head = 0;
public int tail = 0;
public int size = 0;
public MyArrayQueue(int count) {
this.data = new int[count];
}
public MyArrayQueue() {
this.data = new int[10];
}
}
①入队列
java
public void offer(int val){
if(size==data.length){
return ;//直接结束
}
if(tail == data.length){
tail = 0;
}
data[tail] = val;
size++;
}
②.出队列
java
public Integer poll(){
if(size==0){
return null;
}
int res = data[head];
head++;
if(head == data.length){
head = 0;
}
size--;
return res;
}
③.取队首元素
java
public Integer peek(){
if(size==0){
return null;
}
return data[head];
}
2.4以链表模拟队列
首先创建链表节点;
java
class ELinkedNode{
public int val;
public ELinkedNode next;
public ELinkedNode(int val) {
this.val = val;
this.next = null;
}
}
①入队列
java
public class MyLinkedQueue {
ELinkedNode head = null;
ELinkedNode tail = null;
public void offer(int val){
ELinkedNode newNode = new ELinkedNode(val);
if(head == null){
tail = newNode;
head = newNode;
return;
}
tail.next = newNode;
tail = tail.next;
}
}
②出队列
java
public Integer poll(){
if(head==null){
return null;
}
ELinkedNode cur = head;
head = head.next;
return cur.val;
}
③取队首元素
java
public Integer peek(){
if(head==null){
return null;
}
return head.val;
}
三.栈和队列题目
1. 括号匹配

题目分析
1).首先判断符号十分时左括号,如果说,直接入栈
2).还需要判断是否右括号,不然直接返回false;
3).最后依次出栈与右括号比较是否配对
4)返回栈是否为空.为空左右括号都匹配到了
java
public boolean isMatch(char str1,char str2){
if(str1=='('&&str2==')'){
return true;
}
if(str1=='['&&str2==']'){
return true;
}
if(str1=='{'&&str2=='}'){
return true;
}
return false;
}
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for(int i = 0;i<s.length();i++){
char ch = s.charAt(i);
if(ch=='('||ch=='['||ch=='{'){
stack.push(ch);
continue;
}
if(ch!=')'&&ch!='}'&&ch!=']'){
return false;
}
if(stack.empty()){
return false;
}
char str = stack.peek();
if(isMatch(str,ch)){
stack.pop();
continue;
}
return false;
}
return stack.empty();
}

2. 逆波兰表达式求值
题目解析
1).首先判断是否为数字,如果是直接入栈;
2).数字和符号都不是,continue,这个题其实不用考虑,但我们还是要写全部
3).接着判断是加减乘除,拿出两个元素,进行计算

java
public boolean isnumber (String str){
if(str.equals("+")||str.equals("-")||str.equals("*")||str.equals("/")){
return false;
}
return true;
}
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for(String str : tokens){
if(isnumber(str)){
stack.push(Integer.parseInt(str));
continue;
}
if(!stack.empty()){
int res = 0;
int b = stack.pop();
int a = stack.pop();
if(str.equals("+")){
res = a+b;
}
if(str.equals("-")){
res = a-b;
}
if(str.equals("*")){
res = a*b;
}
if(str.equals("/")){
res = a/b;
}
stack.push(res);
}
}
return stack.pop();
}
3. 出栈入栈次序匹配
题目解析:
1).创建一个栈,原来入栈;
2)遍历入栈数组,先入栈,循环判断是否不为空;
3).如果相等,直接出栈,出栈数组往后遍历加1
4).如果不相等,直接结束此次循环;
5).返回栈是否为空
java
public boolean IsPopOrder (int[] pushV, int[] popV) {
// write code here
Stack<Integer> stack = new Stack<>();
int sizepushV = 0;
int sizepopV = 0;
for(;sizepushV<pushV.length;sizepushV++) {
stack.push(pushV[sizepushV]);
while(!stack.empty()){
if(stack.peek()==popV[sizepopV]){
stack.pop();
sizepopV++;
}
else{
break;
}
}
}
return stack.empty();
}

4. 最小栈


可以看到给了一个构造方法用来初始化,然后四个操作方法;
题目解析:
1)首先定义两个栈 ,一个用来正常存储原数据,一个用来存储最小元素的栈
java
public MinStack() {
private Stack<Integer> stack = new Stack<>();
private Stack<Integer> minstack = new Stack<>();
}
2)判断,数值每次存储在satack,如果最小栈为空,存储value;
不断比较min最小值来更新,最后入栈min
java
public void push(int value) {
stack.push(value);
if(minstack.empty()){
minstack.push(value);
return ;
}
int min = minstack.peek();
if(value<min){
min = value;
}
else{
min = min;
}
minstack.push(min);
}

3)出栈
java
public void pop() {
stack.pop();
minstack.pop();
}
4)取栈顶元素
java
public int top() {
return stack.peek();
}
5)取最下栈顶元素
java
public int getMin() {
return minstack.peek();
}
四.面试题
1. 用队列实现栈。

1).首先关键是出栈和去栈;
2).我们需要准备两个队列,一个用来存储元素,当出栈时,将A中的元素不断循环遍历倒腾到B,只剩一个元素就可以出栈了,达到了栈的出栈;
3)取栈顶元素与出栈差不多,只不过多加了一个把最后一个元素还是要倒腾到B中;
java
class MyStack {
public Queue<Integer> A = new LinkedList<>();
public Queue<Integer> B = new LinkedList<>();
public MyStack() {
}
public boolean empty(){
return A.isEmpty()&&B.isEmpty();
}
public void swapAB(){
Queue<Integer> temp = new LinkedList<>();
temp = A;
A = B;
B = temp;
}
public void push(int x) {
A.offer(x);
}
public int pop() {
if(empty()){
return 0;
}
while(A.size()>1){
Integer cur = A.poll();
B.offer(cur);
}
Integer res = A.poll();
swapAB();
return res;
}
public int top() {
if(empty()){
return 0;
}
while(A.size()>1){
Integer cur = A.poll();
B.offer(cur);
}
Integer res = A.poll();
B.offer(res);
swapAB();
return res;
}
}
2. 用栈实现队列。
题目解析:
1)首先创建两个栈,A用来入队列,B用来出队列;
2)检查B是否为空,如果不为空,首先将B的元素倒腾到A里面去,然后再对A进行入栈操作;
3)出栈时遵循后进先出,依次将A中的元素入到B中,B中采用后进先出,此时就达到队列的作用
