栈与队列算法专题

循环队列

在这里插入图片描述
在这里插入图片描述

如果队列可以循环使用,即在一个固定大小的数值中来实现队列,我们即称为循环队列

参数

front:对头下标,存放队列第一个有效数据的下标

rear:存放队尾元素的下一个下标

队列初始化

int[] circularqueue;
int rear;
int front;
public MyCircularQueue(int k) {
        circularqueue=new int[k+1];
        front=1;
        rear=1;
    }

为了区分空队列和满队列:我们将空出一个空间来方便我们判断

这样的话,当我们可以知道当rear==front时,队列为空;(rear+1)%circularqueue.length=front时,队列满了

进队

进队我们就让rear往后移一位,即:rear+1;

但是由于我们是循环队列,rear可以从图中a7回到a1,而rear+1,是无法完成这样的操作,接下来我们就将使用取模的方法实现

public boolean enQueue(int value) {
    //判断队列是否满了
        if(isFull())return false;
        else{
            //由于rear指向的是队尾下一个元素,所以直接将要存放的数值放入到rear下标
            circularqueue[rear]=value;
            //rear的数值是属于[0,circularqueue.length-1],(rear+1)%circularqueue.length,当rear=circularqueue.length-1,再加一,就超出数组下标,但是我们对其取模,就将它再次指向0下标
            rear=(rear+1)%circularqueue.length;
        }
        return true;
    }

出队

出队操作我们让front+1,但由于我们要在队列中循环,所以front+1也要进行取模操作

public boolean deQueue() {
		//队列为空是无法出队
        if(isEmpty())return false;
        else{
        //将对头直接+1,后续进队时,会将出队的位置直接覆盖,所以无需进行其他操作
            front=(front+1)%circularqueue.length;
        }
        return true;
    }

取对头

public int Front() {
        if(isEmpty()){
            return -1;
        }
        else{
            return circularqueue[front];
        }
	}

取队尾

由于队尾的有效元素在rear-1出,而当rear为0时,就会使下标为-1,所以我们需要对其进行加上数组长度再取模操作

public int Rear() {
        if(isEmpty())return -1;
        else{
            return circularqueue[(rear-1+circularqueue.length)%circularqueue.length];
        }
    }

判空

public boolean isEmpty() {
        return rear==front;
    }

判满

public boolean isFull() {
        return (rear+1)%circularqueue.length==front;
    }

栈的压入和弹出顺序

描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。

输入:
[1,2,3,4,5],[4,5,3,2,1]
复制
返回值:
true
复制
说明:
可以通过push(1)=>push(2)=>push(3)=>push(4)=>pop()=>push(5)=>pop()=>pop()=>pop()=>pop()
这样的顺序得到[4,5,3,2,1]这个序列,返回true

思路

我们由题目得知,有两个数组,一个为入栈数组,一个为出栈数组,判断出栈数组,是否符合入栈数组。

这题思路比较简单,我们只需要模拟出栈的顺序就可以判断:

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pushV (入栈数组)int整型一维数组 
     * @param popV (出栈数组)int整型一维数组 
     * @return bool布尔型
     */
    public boolean IsPopOrder (int[] pushV, int[] popV) {
        //申请一个栈用于模拟入栈顺序和出栈顺序
        Stack<Integer>elem=new Stack<>();
        //用于记录入栈数组下标
        int j=0;
		//循环访问入栈数组
        for(int i=0;i<popV.length;i++){
            //当栈为空,或者栈顶元素不等与i下标的出栈元素时,一直将入栈数组对应的元素入栈,模拟入栈
            while(j<popV.length&&(elem.empty()||elem.peek()!=popV[i])){
                elem.push(pushV[j]);
                j++;
            }
            //如果栈顶元素与i下标出栈元素相同,我们就将栈顶pop,模拟栈出。
            if(elem.peek()==popV[i]){
                elem.pop();
            } 
        }
        //当最后栈为空,就说明出栈顺序没有问题
        if(elem.empty())return true;
        return false;
    }
}

逆波兰表达式

描述

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

  • 有效的算符为 '+''-''*''/'

  • 每个操作数(运算对象)都可以是一个整数或者另一个表达式。

  • 两个整数之间的除法总是 向零截断

  • 表达式中不含除零运算。

  • 输入是一个根据逆波兰表示法表示的算术表达式。

  • 答案及所有中间计算结果可以用 32 位 整数表示。

  • 输入:tokens = ["2","1","+","3","*"]
    输出:9
    解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
    

思路

这个问题的解决思路相对比较简单,困难的是知道什么事逆波兰表达式,各位看官可自行了解,这里便不多赘述。

当我们遇到数字时,将数字入栈

遇到运算符时,取出栈顶元素p2,然后再取出栈顶元素p1,p2为运算符的右值,p1为左值,得出的结果入栈,重复此操作,直至将表达式遍历完成,最后的结果就是栈留下的最后一个元素。

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> elem=new Stack<>();
        for(int i=0;i<tokens.length;i++){
            //取出表达式
            String temp=tokens[i];
            //当为运算符时
            if(temp.equals("-")){
                int p2=elem.pop();
                int p1=elem.pop();
                elem.push(p1-p2);
            }
            else if(temp.equals("*")){
                int p2=elem.pop();
                int p1=elem.pop();
                elem.push(p1*p2);
            }else if(temp.equals("+")){
                int p2=elem.pop();
                int p1=elem.pop();
                elem.push(p1+p2);
            }else if(temp.equals("/")){
                int p2=elem.pop();
                int p1=elem.pop();
                elem.push(p1/p2);
            }
            //当是数字的时候
            else{
                elem.push(Integer.valueOf(temp));
            }
        }  
        //最后一个元素就是表达式结果
        return elem.pop();
    }

}

用栈模拟实现队列

描述

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty):

输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

思路

我们使用两个栈来辅助我们实现队列,一个是作为入队用的栈stack1,一个用作出队的栈stack2

当我们入队时,直接将数据入栈到stack1中

出队时,如果stack2 为空,将stack中的数据出栈并入栈到stack2中,再出栈stack2

如果不为空,直接出栈stack2

class MyQueue {
    Stack<Integer>stack1;
    Stack<Integer>stack2;
    public MyQueue() {
        stack1=new Stack<>();
        stack2=new Stack<>();
    }
    
    public void push(int x) {
        stack1.push(x);
    }
    
    public int pop() {
        	//队列为空直接返回-1
        if(empty())return -1;
        else{
                //至少有一个栈不为空
            if(stack2.empty()){
                //将stack1的数据入到stack2
                while(!stack1.empty()){
                    stack2.push(stack1.pop());
                }
                //stack2栈顶就是队列头元素
                return stack2.pop();
            }else{
                return stack2.pop();
            }
        }
    }
    
    public int peek() {
    	//队列为空直接返回-1
        if(empty())return -1;
        //至少有一个栈不为空
        else{
        	//stack1不为空时
             if(stack2.empty()){
             //将stack1的数据入到stack2
                 while(!stack1.empty()){
                    stack2.push(stack1.pop());
                }
               //stack2栈顶就是队列头元素
                return stack2.peek();
            }else{
                return stack2.peek();
            }
        }
    }
    //当1、2两个栈都为空时,队列为空
    public boolean empty() {
        return stack1.empty()&&stack2.empty();
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值