栈(栈的模拟实现,栈OJ题)

目录

概念

栈的方法

栈的模拟实现

栈面试题

1.若进栈序列为1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈系列是()

2.打印列表:

递归方式:

栈的方式逆转打印

3.括号匹配

4.逆波兰表达式 

5. 栈的压入、弹出序列

6.最小栈


栈继承父类Vector里有Object[] 数组,栈的底层是用数组实现的。 

概念

:一种特殊的线性表,其 只允许在固定的一端进行插入和删除元素操作 。进行数据插入和删除操作的一端称为栈
顶,另一端称为栈底。栈中的数据元素遵守后进先出 LIFO Last In First Out )的原则。
压栈:栈的插入操作叫做进栈 / 压栈 / 入栈, 入数据在栈顶
出栈:栈的删除操作叫做出栈。 出数据在栈顶

栈的方法

push():入栈

peek():获取栈顶元素,但是不删除(偷偷瞄一眼)

pop():出栈,删除栈顶元素

栈的模拟实现

基于数组实现栈定义类

public class MyStack {
    //数组
    private int [] elem;
    //定义usedSize记录栈的元素个数,方便出栈入栈。
    private int  usedSize;
    private static final int DEFAULT_CAPACITY = 10;
    private MyStack() {
        this.elem = new int[DEFAULT_CAPACITY];
    }
}

数组是从零开始的

usedSize和elem数组下标差值为1!!!

在实现pop()方法时,我们直接通过usedSize--即可

例如下图我们要出栈顶元素56,此时usedSize值为 3 ,让usedSize-1

即可达到出栈效果,在下一次新的元素入栈时可以把 usedSize(2) 这里的元素替代掉

import java.util.Arrays;

public class MyStack {
    private int [] elem;
    private int  usedSize;
    private static final int DEFAULT_CAPACITY = 10;
    private MyStack() {
        this.elem = new int[DEFAULT_CAPACITY];
    }
    public void push(int val) {
        if(isFull()) {
            //如果,满了,扩容
            this.elem = Arrays.copyOf(elem,usedSize*2);
        }
        this.elem[usedSize] = val;
        this.usedSize++;
    }
    //判断是否为满
    public boolean isFull() {
        return this.usedSize==this.elem.length;
    }
    //不是真正的出栈  因为通过usedSize入栈
    // usedSide--
    // 后面进入栈是通过usedSize 操作的,在usedSide--后,入栈的新元素,会把,栈顶元素替代掉
    // 相当于 让栈顶元素出栈
    public int pop(int val) {
        if(isEmpty()) {
            throw new EmptyException("栈为空");
        }
        //拿出要出栈的元素
        int ret = this.elem[this.usedSize-1];
        //更新实际存在元素
        this.usedSize--;
        return ret;
    }
    public int peek() {
        if(isEmpty()) {
            throw new EmptyException("栈为空");
        }
        return elem[usedSize-1];
    }
    public boolean isEmpty() {
        return this.usedSize == 0;
    }


}

//自定义异常:
public class EmptyException extends RuntimeException{
    public EmptyException() {
    }
    public EmptyException(String message) {
        System.out.println(message);
    }
}

栈的模拟实现不难,更具有挑战性的是栈的笔试题,编程题。

栈面试题

1.若进栈序列为1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈系列是()

A.1,2,3,4               B. 2,3,4,1          C. 3,1,4,2          D. 3,4,2,1

画图带入演示即可:

例如A项 (题中说明进栈过程可以出栈)

1.1进栈,1出栈

2.2进栈,2出栈

后面同理,所以出栈序列可以是1,2,3,4

2.打印列表:

逆序打印:

递归方式:

void printList(Node head) {
    if(null!=head) {
      printList(head.next);
      System.out.print(head.val+"");
   }
}

在递归调用printList方法是系统申请了栈:

第一次四次进入printList时,head.next为空,开始打印45,以此一直返回带最开始

类似于最后进栈的45最先出的的栈的数据结构(在栈上开辟栈)

栈的方式逆转打印

    //逆转打印链表
    public void reversePrintList() {
        Stack<ListNode> stack = new Stack<>();
        ListNode cur = head;
        //先入栈
        while (cur!=null) {
            stack.push(cur);
            cur = cur.next;
        }
        //出栈就为逆序了
        while (stack.isEmpty()) {
            ListNode top = stack.pop();
            System.out.println(stack.pop().val);
        }
    }

3.括号匹配

题目链接:

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。icon-default.png?t=N7T8https://leetcode.cn/problems/valid-parentheses/题目描述:

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。  

例子:

思路:

带入示例遍历可以总结:第一个遇到的右括号,要和最后一个左括号匹配 

所以我们可以借助栈这种数据结构,在遍历整个括号字符串时,遇到左边的括号,往栈里存。

在栈不为空的前提下,遇到右括号时出栈。

当遍历完全部时,栈不为空,说明栈里还有为和其匹配的括号。 


代码实现:

class Solution {
        public static 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);
            }else{
                // else 里的情况 说明是右括号,栈为空,说明栈里没有和右括号匹配的
                if(stack.empty()) {
                    return false;
                }
                // top 拿的是栈里左括号  ch 拿的是右括号
                char top = stack.peek();
                if(top == '(' && ch==')'||top == '{' && ch=='}'||top == '[' && ch==']') {
                    stack.pop();
                }else{
                    return false;
                }
            }
        }
        if(!stack.empty()) {
            return false;
        }
        return true;
    
    }
}

4.逆波兰表达式 

题目链接:

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。icon-default.png?t=N7T8https://leetcode.cn/problems/evaluate-reverse-polish-notation/题目描述:

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

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

注意:

  • 有效的算符为 '+''-''*' 和 '/' 。
  • 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
  • 两个整数之间的除法总是 向零截断 。
  • 表达式中不含除零运算。
  • 输入是一个根据逆波兰表示法表示的算术表达式。
  • 答案及所有中间计算结果可以用 32 位 整数表示。

思路:

中缀表达式:(1+2)*5  平时我们最常见的)

后缀表达式:1 2 + 5 *  

 中缀表达式变后缀表达式的方法:  

第一步:先乘除后加减的添加括号

例如  (1+2)*5    先对向 * 的两个整体加上括号  ((1+2)*5 ) 因为 1+2 已经有括号了,就不需要加了

第二步:把运算符(+-*/) 往离它最近的括号后(右)移

(如果要变换为前缀表达式,就往离它最近的括号前(左) ——》((12)+5 )*

第三步: 把全部括号清除 

(12)+5 )* ——》 12+5*

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for(String s :tokens) {
            if(isNum(s)) {
                //这里拿到的是字符串 通过Integer.parseInt(s) 可以转为int
                stack.push(Integer.parseInt(s));
            }else{
                //出栈的第一个数字为右操作数
                int num2 = stack.pop();
                int num1 = stack.pop();
                switch(s)
                {
                    case "+":
                        stack.push(num1 + num2);
                        break;
                    case "-":
                        stack.push(num1 - num2);     
                        break;
                    case "*":
                        stack.push(num1 * num2);                    
                        break;
                    case "/":
                        stack.push(num1 / num2);
                        break;
                }
            }

        }
     //最后栈底为最终的结果
        return stack.pop();
    }
    public boolean isNum(String s) {
        if((s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/")) {
            return false;
        }
        return true;
    }
}

5. 栈的压入、弹出序列

题目链接:

栈的压入、弹出序列_牛客题霸_牛客网

题目描述:

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

1. 0<=pushV.length == popV.length <=1000

2. -1000<=pushV[i]<=1000

3. pushV 的所有数字均不相同

示例:

示例1

输入:

[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

示例2

输入:

[1,2,3,4,5],[4,3,5,1,2]

返回值:

false

说明:

由于是[1,2,3,4,5]的压入顺序,[4,3,5,1,2]的弹出顺序,要求4,3,5必须在1,2前压入,且1,2不能弹出,但是这样压入的顺序,1又不能在2之前弹出,所以无法形成的,返回false 

思路:此题的选择题形式和上述的第一题一样,我们需通过代码判断

代码实现:

import java.util.*;


public class Solution {

    public boolean IsPopOrder (int[] pushV, int[] popV) {
        Stack<Integer> stack = new Stack<>(); 
        int j = 0;
       // 这里第一次敲,把j放for循环里定义了,导致部分不通过
        for(int i = 0; i < pushV.length;i++) {
            stack.push(pushV[i]);      
            while(!stack.empty()&&j<popV.length
            &&stack.peek()==popV[j]) {
            stack.pop();
            j++;
            }
        }
        return stack.empty();
    }
}

6.最小栈

题目链接:

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。icon-default.png?t=N7T8https://leetcode.cn/problems/min-stack/题目描述:

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素。  

示例:

示例 1:

输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

输出:
[null,null,null,null,-3,null,0,-2]

解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.

提示:

  • -231 <= val <= 231 - 1
  • poptop 和 getMin 操作总是在 非空栈 上调用
  • pushpoptop, and getMin最多被调用 3 * 104 次

思路:

入栈:push()

题中提到在常数时间内检索到最小元素的栈,即时间复杂度为O(1)。

我们可以考虑使用两个栈

一个正常输入,另外一个存最小值

代码实现:

import java.util.Stack;
class MinStack {
    private Stack<Integer> stack;
    private Stack<Integer> minStack;
    public MinStack() {
        stack = new Stack<>();
        minStack = new Stack<>();
    }
    
    public void push(int val) {
        stack.push(val);
        if(minStack.empty()) {
            minStack.push(val);
        }else{
            if(val<=minStack.peek()) {
                minStack.push(val);
            }
        }

    }  
    public void pop() {
        
        if(!stack.empty()) {
          int ret = stack.pop();
          if(minStack.peek()==ret) {
              minStack.pop();
          }
        }
        
    }
    
    public int top() {
        if(stack.empty()) {
            return -1;
        }
        return stack.peek();
    }
    
    public int getMin() {
      if(minStack.empty()) {
            return -1;
        }
        return minStack.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(val);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.getMin();
 */

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值