栈与队列最优解总结

栈与队列

实现最小栈

题目描述

  • 实现一个特殊的栈,在实现栈的基本功能的基础上,再返回栈的最小元素的操作
  • 要求:pop(),push(),getMin()时间复杂度都是O(1)

解题思路

  • 设计上使用两个栈,一个栈保存当前栈中的元素记为stackData;另外一个栈用于保存每一步的最小值,记为minStack;
    1. 压入数据规则
      设当前数据为num,先将其压入StackData,然后判断minStack是否为空,若为空,压入minstack,否则比较num与minStack的栈顶元素,当小于栈顶元素时,压入minStack
    2. 弹出数据规则
      先在stackData中弹出栈顶元素,记为value,然后比较minStack中的栈顶元素,如果minStack栈顶元素等于value,minStack的栈顶元素也弹出,否则不进行处理
    3. 查询最小值
      minStack始终保存stackData中的最小值
public class Stack1{
    private Stack<Integer>dataStack;
    private Stack<Integer> minStack;

    public void push(int num){
        if(this.minStack.isEmpty()){
            this.minStack.push(num);
        }else if(num<=this.getMin()){
            this.minStack.push(num);
        }
        this.dataStack.push(num);
    }
    public int pop(){
        int value=this.dataStack.pop();
        if(value==this.minStack.peek()){
            this.minStack.pop();
        }
        return value;
    }
    public int getMin(){
        return this.minStack.peek();
    }
}

由两个栈组成的队列

题目描述

编写一个类,用两个栈实现队列,支持队列的基本操作(add,poll,peek)

解题思路

由于栈的特点是先入后出,队列的特点是先进先出,使用两个栈正好能把顺序反过来;设计上使用两个栈,一个栈负责压入数据,记为pushStack,另外一个栈负责弹出数据popStack;
1. 压入数据的规则
pushStack正常压入数据,但是如果popStack不为空,不可将pushStack的数据压入到popStack;同时pushStack的数据要往popStack压入数据时,必须要一次性的将pushStack数据全部压入,add方法
2. 弹出数据规则
popStack()正常弹出数据即可。即poll,与peek方法,
3. 向popStack压入数据在poll,peek方法处执行

public class TwoStackQueue{
    private Stack<Integer> pushStack;
    private Stack<Integer> popStack;

    public void add(int num){
        pushStack.push(num);
    }
    public int poll(){
        if(pushStack.empty()&&stackPush.empty()){
            throw new RuntimeException("Queue is Empty");
        }
        else if(popStack.empty()){
            while(!pushStack.empty()){
                popStack.push(pushStack.pop());
            }
        }
        return popStack.pop();
    }
    public int peek(){
        if(pushStack.empty()&&stackPush.empty()){
            throw new RuntimeException("Queue is Empty");
        }
        else if(popStack.empty()){
            while(!pushStack.empty()){
                popStack.push(pushStack.pop());
            }
        }
        return popStack.peek();
    }
}

两个队列实现栈

题目描述

编写一个类,用两个队列实现栈的功能,实现栈的push,pop方法

解题思路

定义两个队列queue1,queue2
1. push方法实现
判断queue1,queue2是否为空,将需要进栈的元素num,添加到不为空的一个队列的队尾,
2. pop方法实现
判断queue1,queue2是否为空,为说明方便,假设queue1队列不为空,要获得栈顶元素(队尾的元素,记为value),将队列中的元素,从队首到队尾的前一个元素依次弹出,添加到queue2(初始状态为空队列)队尾位置,最后弹出queue1队尾元素value,此时queue2队列为空,

public class TwoQueueStack{
    private LinkedList<Integer> queue1;
    private LinkedList<Integer> queue2;

    public void push(int num){
        if(queue1.isEmpty()){
            queue2.add(num);
        }
        if(queue2.isEmpty()){
            queue2.add(num);
        }
    }
    public int pop(){
        if(queue2.isEmpty()&&queue2.isEmpty()){
            throw new RuntimeException("Stack is Empty");
        }
        if(queue1.isEmpty()){
            while(queue2.size()>1){
                queue1.add(queue2.poll());
            }
            int value= queue2.poll();
        }
        else if{//queue2.isEmpty()
            while(queue1.size()>1){
                queue1.add(queue1.poll());
            }
            int value= queue1.poll();
        }
        return value;
    }
}

使用递归函数和栈操作逆序一个栈

题目描述

只能用递归函数来实现栈的逆序的操作

思路解析

两个递归函数:
- 递归函数1:将栈的栈底元素删除并且返回
- 递归函数2:逆序一个栈,将栈底元素删除之后的栈进行逆序,然后将栈底元素重新压入栈中

public class ReverseStack{
    private Stack<Integer> stack;

    public int getAndRemoveLastElement(){
        int result=stack.pop();
        if(stack.isEmpty()){
            return result;
        }else{
            int res=getAndRemoveLastElement();
            stack.push(result);
            return res;
        }
    }
    public void reverseStack(){
        if(stack.isEmpty()){
            return;
        }
        int res=getAndRemoveLastElement();
        reverseStack();
        stack.push(res);
    }
}

用一个栈实现另一个栈的排序

题目描述

一个栈中的元素类型为整型,将该栈从栈顶到栈底从大到小进行排序,只允许申请一个栈,除此之外,可以申请新的变量,不能申请额外的数据结构

思路解析

将要排序的栈记为stack,辅助栈为help,比较操作在help栈中实现,具体操作如下:
1. 在stack进行pop操作,弹出的元素记为cur
2. 比较cur,help的栈顶元素
- 如果cur小于help的栈顶元素,直接将cur压入help栈中
- 如果cur大于help的栈顶元素,则将help栈中的元素逐一弹出,逐一压入stack中,直到cur小于或者等于help的栈顶元素;
3. 最后将help的元素压入stack中

public void sortStackByStack(Stack<Integer> stack){
    Stack<Integer> help=new Stack<>();
    while(!stack.isEmpty()){
        int cur=stack.pop();
        while(!help.isEmpty()&&cur>help.peek()){
            stack.push(help.pop());
        }
        help.push(cur);
    }
    while(!help.isEmpty()){
        stack.push(help.pop());
    }
}

生成窗口最大值数组

题目描述

有一个整型数组arr,和一个大小为w的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置。

例如arr=[4,3,5,4,3,3,6,7],窗口大小为3
[4  3  5] 4  3  3  6  7  窗口的最大值为5
 4 [3  5  4] 3  3  6  7  窗口的最大值为5
 4  3 [5  4  3] 3  6  7  窗口的最大值为5
 4  3  5 [4  3  3] 6  7  窗口的最大值为4
 4  3  5  4 [3  3  6] 7  窗口的最大值为6
 4  3  5  4  3  [3 6  7] 窗口的最大值为7
数组长度为n,窗口为w,将产生n-w+1
输出为:[5 5 5 4 6 7]

思路解析

使用双端队列实现窗口最大值的更新,双端队列qmax保存的是数组的索引。队首即为窗口的最大值的索引。
1. 假设遍历到arr[i],qmax的放入规,则:
- qmax为空,直接放入i,
- qmax不为空,取出当前qmax的队尾存放的下标j(其实是队列的最小值)
- 如果arr[i]>=arr[j],直接将j从qmax队列弹出,继续qmax的放入规则,说明arr[j]不可能是之后向右滑动窗口的最大值
- 如果arr[j]>arr[i],将小标i,加入到qmax队尾中,由于arr[i]可能是之后向右滑动窗口的最大值,
2. 要清除队列中最大值的失效值。当遍历的数组元素下标i,qmax队首元素为j,窗口的长度w,之间满足j=i-w;也就是元素arr[j]最大值已经失效了
3. 只要遍历数组元素下标i满足i>w-1;将qmax队首元素为某个窗口的最大值,保存即可。

public int[] getMaxWindow(int[]arr,int w){
    if(arr==null||arr.length<w||w<0)
        return null;
    int index=0;
    int res=new int[arr.length-w+1];
    LinkedList<Integer> qmax=new LinkedList<>();
    for(int i=0;i<arr.length;i++){
    while(!qmax.isEmpty()&&arr[i]>=arr[qmax.peekLast()])
    {
            qmax.pollLast();
    }
    qmax.addLast(i);
    if(i-w==qmax.peekFirst())//清除失效最大值
        qmax.pollFirst();
    if(i>w-1)
        res[index++]=arr[qmax.peekFirst()];
    }
    return res;
}

求最大子矩阵的大小

题目描述

给定一个整型矩阵map,其中的值只有0,1;求其中全是1的所有矩形区域中,最大的矩形区域为1的数量。

例如 1 1 1 0 ,最大的矩形区域有3个1,返回1
map=[1 0 1 1 
     1 1 0 1 
     1 1 1 1]
其中最大的矩形区域有4个1,返回4

思路解析

如果矩阵的大小为O(NM),本题的时间复杂度为O(NM);
1. 矩阵的行数为N,以每一行做切割,统计以当前作为底的情况,每个位置上为1的数量,使用高度数组height表示,
- 以题目描述的map为例,从第一行做切割,height=[1 0 1 1]
- 以第二行做切割,更新height;height=[2 1 0 2];height的更新操作为height[i]=map[i][j]==0?0:height[j]+1;
- 以第三行做切割,更新height;height=[3 2 1 3]
2. 对于每次切割,利用更新的height数组求出以每一行为底的情况下,最大的矩形是什么
- 求解当前height数组组成的最大矩形,比如{3,2 ,3, 1},看出一个数组组成的直方图
1. 第1根高度为3的柱子无法向左扩展,右边为2, 也无法向右左扩展,则以第1根柱子为高度的矩形面积为3*1=3;
2. 第2根高度为2的柱子向左可以扩展一个距离,向右扩展1个距离,则以第2根柱子为高度的矩形面积为2*3=6;
3. 同理,以第3根柱子为高度的矩形面积为3*1=3
4. 同理,以第4根柱子为高度的矩形面积为1*4=4

 - 使用栈来实现上述操作

     1. 生成一个栈,记为stack,每遍历一个位置都会把位置压入到stack中
     2. 当遍历到arr[i],栈为空时,直接压入,若栈不为空,比较栈顶位置所代表的值与arr[i]大小,若arr[i]较大,直接压入stack中;否则则把栈中存的位置不断弹出,直到某一个栈顶所代表的值小于arr[i],再把位置i压入
     3. 在遍历到数组arr[i],弹出的栈顶位置为j,弹出栈顶之后,新的栈顶元素为k,那么可以知道以arr[j]为高度的柱子向右最多可以扩展到(i-1),因为j之所以被弹出,是由于遇到了**第一个**比位置j值小的位置;向左最多扩展到k+1;这是因为栈中k,j位置是相邻的,并且从栈顶到栈底的位置所代表的的值依次递减并且无重复的,由于k位置在栈中,arr[k+1...j-1]都是既大于arr[k],否则k会被弹出;同时由于j在栈中,并且与k相邻,所以arr[k+1...j-1]没有小于arr[j],否则j不可能与k相邻。
     4. j位置的柱子能扩出来的最大矩形为(i-k-1)arr[j]
public int maxSize(int[][]map){
    if(map==null|||map.length==0||map[0].length==0)
        return 0;
    int maxArea=0;
    int []height=new int[map[0].length];
    for(int i=0;i<map.length;i++){
        for(int j=0;j<map[0].length;j++){
            height[j]=map[i][j]==0?0:height[j]+1;
        }
        maxArea=Math.max(maxArea,maxSizeFromBottom(height));
    }
    return maxArea;
}
public int maxSizeFromBottom(int [] height){
    if(height==null||height.length==0)
        return 0;
    int maxArea=0;
    Stack<Integer> stack=new Stack<>();
    for(int i=0;i<height.length;i++){
        while(!stack.isEmpty()&&height[i]<=height[stack.peek()]){
            int j=stack.pop();
            int k=stack.isEmpty()?-1:stack.peek();
            int cur=height[j]*(i-k-1);
            maxArea=Math.max(cur,maxArea);
        }
        stack.push(i);
    }
        while(!stack.isEmpty()){
            int j=stack.pop();
            int k=stack.isEmpty()?-1:stack.peek();
            int cur=(height.length-k-1)*height[j];
            maxArea=Math.max(cur,maxArea);
        }
        reuturn maxArea;
}

最大值值减去最小值小于或等于num的子数组的数量

题目描述

给定数组arr和整数num,共返回多少个数组子数组的满足如下情况:
max(arr[i...j])-min(arr[i..j])<=num
要求:
    如果数组的长度为N,时间复杂度为ON)的解法

思路解析

使用双端队列实现,qmax维护窗口arr[i…j]的最大值更新的结构,qmin维护窗口子数组arr[i..j]最小值更新结构,
两个基本的结论:
- 如果子数组arr[i…j]满足条件,那么arr[i..j]的子数组arrk…l都满足条件
- 反之arr[i…j]不满足条件,那么包含该数组arr[i..j]的子数组必然不满足条件。

具体实现步骤:

  1. 生成双端队列qmax,qmin.生成两个整型变量i,j表示子数组的arr[i..j]
  2. 令j不断向右移动位置,不断跟新qmax,qmin结构,保证qmax,qmin位置窗口的最大值与最小值,一旦出现arr[i…j]不满足条件,说明arr[i…j-1],arr[i..j-2]….arr[i…i]都是满足条件的
  3. 执行完步骤2,令i++;并对qmax,qmin进行调整,qmax,qmin变为arr[i+1…j]的最大值,最小值的更新结构
  4. 根据步骤2,3,得到以arr[0]….arr[N-1]作为第一个元素的子数组满足条件的数量分别是多少
public int getNum(int[] arr,int num){
    if(arr==null||arr.length==0){
        return 0;
    }
    LinkedList<Integer> qmin=new LinkedList<>();
    LinkedList<Integer> qmax=new LinkedList<>();
    int i=0,j=0;
    int res=0;
    while(i<arr。length){
        while(j<arr.length){
            while(!qmin.isEmpty()&&arr[j]<=arr[qmin.peekLast()]){
                qmin.pollLast();
        }
            qmin.addLast(j);
            while(!qmax.isEmpty()&&arr[j]>=arr[qmax.peekLast()]){
                qmax.pollLast();
            }
            qmax.addLast(j);
            if(arr[qmax.peekFirst()]-arr[qmin.peekFirst()]>num)
                break;
            j++;
        }
        if(qmin.peekFist()==i)
            qmin.pollFirst();
        if(qmax.peekFirst()==i)
            qmax.pollFirst();
        res+=j-i;
        i++;
    }
    return res;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 发现错误并改错。给出一个实现插入排序的程序,文件名为InsertionSort.java,该文件中有代码错误,请单步调试,发现错误, 并改正错误,提示:只有一行程序有错误,所以只能修改代码中的某一行程序。要求演示在eclipse开发环境下 单步调试程序,如果没有eclipse开发环境,也可以使用其它集成开发环境,但是必须演示调试能力。 2. 请编写程序输出13/17小数点后第100位的数字是几?考察循环和除法运算以及模运算(求余数运算)。 请补齐FractionalDigit.java文件中的代码 3. 请编写程序,分别使用循环和递归两种方法计算斐波那契数列第9项是几? 斐波那契数列的第1项为1,第2项为1,后续任意项的值为其前两项的和。 请补齐Fibo.java文件中的代码 4. 实现Array类中的indexOf函数; 阅读Node.java和SingleLinkedList.java两个关于单链表的类, 实现SingleLinkedList类中的indexOf函数; 5. 阅读Node.java和SingleLinkedList.java类,利用它们来实现和双向队列功能,请阅读Stack.java 和Deque.java(双向队列),请实现Stack类中的pop函数,请实现Deque.java中的removeLast函数。 6. 利用前面的Stack.java和Deque.java类,实现下面问题的求解: 队列中有从1到7(由小到大排列)的7个整数,问经过一个整数后,出的所有排列数有多少? 如果整数的容量是4(最多能容纳4个整数),那么出的排列数又是多少?(百度查询 卡特兰数) 请补齐Catalan.java文件中的代码 7. 八皇后问题。在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、 同一列或同一斜线上,问有多少种摆法。该程序代码在EightQueens.java文件中,请将void place(int n)函数 中未完成的代码补齐,并写出运行结果。 禁止修改其他代码。 8. 学生排序。给出4名同学,请按照学生的年龄排序,然后输出学生的学号和姓名。具体代码见Student.java文件, 请实现sort函数。并运行该程序。 9. 二叉排序树检索。BstNode.java和BinarySortTree.java是关于二叉排序树的程序文件,请阅读这两个程序 文件,请实现contains函数,并运行该程序。 10. BASE64编码转换问题。请百度百科查询base64编码,实现将二进制字节数据编码转换为base64的字符串数据,以及 将base64字符串转换回二进制字节数据。禁止使用JDK自带的Base64转换程序以及Apache开源网站上的程序, 必须自己编写代码来实现BASE64编码和解码函数。可以为该类添加其他数据成员和函数成员。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值