【Java数据结构--栈原来是这样的】

1.栈的基本认识

栈的结构如图所示:栈本身就是一个数组
在这里插入图片描述
我们发现,栈的结构和顺便表,链表有所差异,主要是因为栈对元素的存取有所特殊要求.

🧐

1.栈只能从一段存取数据,另一端是封闭的
2.在栈中,存取数据都要遵守先进后出的原则,也就是最先进栈的最后出栈,最后进栈的最先出先出栈.

1.1 栈的定义

栈是一种只能从一段存取数据且遵循"先进后出"原则的线性存储结构.
栈的开口端被称为栈顶,闭口端称为栈底.

在了解了栈的基本结构之后,那么栈是如何实现元素的增加和删除呢?

😃删除一个元素:也就是把栈顶元素移开,让该元素下面的元素作为新的栈顶. 也叫做入栈

😃增加一个元素:让新增的元素变为新的栈顶.也叫做出栈

2.栈的模拟实现

实现代码:

public class MyStack {
    public int[] elem;
    public int usedSize;//默认0
    MyStack(){//栈的初始化
        elem=new int[]{1,2,3,4};
        usedSize=elem.length;
    }
    //判断是否为空
    public boolean isEmpty(){
        if(usedSize==0){
            return true;
        }
        return false;
    }
    //判断栈是否满了
    public boolean ifFull(){
        return usedSize==elem.length;
    }
    //入栈
    public void push(int val){
        if(ifFull()){
            elem= Arrays.copyOf(elem,2*elem.length);//2倍扩容
        }
        elem[usedSize]=val;
        usedSize++;
    }
    //出栈
    public int pop(){
       if(isEmpty()){
           System.out.println("栈为空,无法出栈");
           return -1;
       }
       int val=elem[usedSize-1];
       usedSize--;
       return val;
    }
    //获取栈顶元素,只是获取,并不出栈
    public int peek(){
        if(isEmpty()){
            System.out.println("栈为空,不能获取");
            return -999;
        }
        return elem[usedSize-1];
    }
    //获取栈中元素个数
    public int size(){
        return usedSize;
    }
    //打印栈中所有元素
    public void printMyStack(){
        for(int i=0;i<usedSize;i++){
            System.out.print(elem[i]);
        }
        System.out.println();
    }
}

测试代码:

public class Test1 {
    public static void main(String[] args) {
        MyStack myStack=new MyStack();
        myStack.printMyStack();
        System.out.println("==================");
        myStack.pop();
        myStack.printMyStack();
        System.out.println("==================");
        myStack.push(8);
        myStack.printMyStack();
        System.out.println("==================");
        System.out.println("栈中此时的元素个数是"+myStack.size());
    }
}

结果:

1234
==================
123
==================
1238
==================
栈中此时的元素个数是4

3. 相关练习题

😎有效的括号 OJ链接

大体思路:

1.遍历字符串,遇到左括号则入栈,遇到右括号,则将两个括号进行匹配判断,如果匹配,则出栈,然后继续遍历,如果不匹配,则字符串无效,直接返回.

💙当字符串遍历完,栈正好也为空,则有效 💙当字符串遍历完,栈不为空,说明左括号多,无效
💙如果字符串还没遍历完,栈就为空,说明右括号多,无效

代码:

public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        String s=sc.nextLine();
        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 {//如果遇到的是右括号
                if(stack.empty()){//如果栈为空,说明右括号多,无效
                    return;
                }
                char tmp=stack.peek();//就将左括号出栈并与这个右括号比较,看是否匹配
                //如果不匹配,那么这个字符串无效
                if(ch==')'&&tmp=='('){
                    stack.pop();
                } else if (ch=='}'&&tmp=='{') {
                    stack.pop();
                } else if (ch==']'&&tmp=='[') {
                    stack.pop();
                }else {
                    System.out.println("不是有效字符串");
                    return;
                }
                //如果匹配,那么继续遍历字符串,重复上述步骤
            }
        }
        if(!stack.empty()){//如果字符串遍历完,栈不为空,说明左括号更多,这些括号多于出来,无法匹配,说明是无效的
            System.out.println("不是有效字符串");
            return;
        }
        System.out.println("是有效字符串");
    }

3.2 补充:递归打印链表

public void disPlay(ListNode head){
       if(head==null){
           return;
  }
  if(head.next==null){
        System.out.print(head.val);
  }
  disPlay(head.next);
  System.out.print(head.val+" ");
}

3.3 利用栈打印链表

public void disPlay(){
     Stack<ListNode> stack=new Stack<>();
     ListNode cur=head;
     while(cur!=null){
         stack.push(cur);
         cur=cur.next;
    }
    while(!stack.empty()){
         ListNode top=stack.pop();
         System.out.print(top.val+" ");
   }
}

😎逆波兰表达式 OJ链接

大体思路:
1.什么是逆波兰表达式?
逆波兰表达式,也就是没有括号,运算符总是放在和它相关的操作数之后因此逆波兰表达式也叫做后缀表达式.

举个例子🍔:将中缀表达式,转换为后缀表达式
中缀表达式"

1+((2+3)*4)-5

"
转换为后缀表达式,就是"

123+4*5-

"

2.遍历字符串数组,若遇到数字,则将数字入栈,如果遇到运算符,则出栈(出两个元素),将第一个出栈的元素放在运算符右侧,第二个出栈的元素放在运算符左侧.(这个顺序很重要).然后将运算得到的结果入栈.整个数组遍历结束后,栈内只有一个元素,也就是逆波兰表达式的值.

在这里插入图片描述

代码:

//逆波兰表达式求值
    //遍历数组,只要是数字就入栈,遇到运算符,就出两个栈里的元素,并且把先出的放在运算符右侧,后出的放在运算符左侧,将计算结果再次入栈
    public static void main(String[] args) {
        //给定一个字符串数组,由这个字符数组得到一个表达式,最后得出表达式的值
        String[] tokens={"2","1","+","3","*"};
        //给定一个栈
        Stack<Integer> stack=new Stack<>();
        //遍历字符串数组
        for(int i=0;i<tokens.length;i++){
            String s=tokens[i];
            if(tokens[i].equals("+")||tokens[i].equals("-")||tokens[i].equals("*")||tokens[i].equals("/")){//遇到运算符就出栈
                int right=stack.pop();
                int left=stack.pop();
                switch (s){
                    case "+":{
                        stack.push(left+right);
                        break;//注意break
                    }
                    case "-":{
                        stack.push(left-right);
                        break;
                    }
                    case "*":{
                        stack.push(left*right);
                        break;
                    }
                    case "/":{
                        stack.push(left/right);
                        break;
                    }
                }
            }
            else {
                stack.push(Integer.parseInt(tokens[i]));//转成整数
            }
        }
        System.out.println(stack.peek());//栈中剩最后的结果
    }

😎栈的压入弹出序列 OJ链接

代码:

public static void main(String[] args) {
        int[] push= new int[]{1, 2, 3, 4, 5};
        int[] pop=new int[]{4,5,3,2,1};
        int j=0;
        Stack<Integer> stack=new Stack<>();
        for(int i=0;i<push.length;i++){
            stack.push(push[i]);
            while(j<pop.length&&!stack.empty()&&stack.peek().equals(pop[j])){
                stack.pop();
                j++;
            }
        }
        boolean ret=stack.empty();
        System.out.println(ret);
    }

😎最小栈 OJ链接

😉这一题的关键之处在于"要在常数时间内找到栈中此时最小的元素".解决方法就是,借助一个辅助栈,让一个栈中的最小元素始终对应辅助栈的栈顶元素.

🤨我们举个例子说明一下:

假设我们要让元素3,4,1,6,2依次入栈,我们定义两个栈,一个正常出入栈,另一个辅助栈用来存放正常栈的不同时刻对应的最小的元素.在这里插入图片描述

元素3,4,1,6,2都要依次入stack栈,而minSatck不会让所有的元素入栈,而是要让不同时刻对应的最小的元素入minStack栈.当两个栈都为空时,3这个元素两个栈都入,再要入4这个元素的时候,要用4去和minStack栈里的元素去比较,如果这个数比minStack的栈顶元素小于或者等于,那么就两个栈都入,如果这个数大于minStack的栈顶元素,那么这个数就只入stack这个栈.

代码:

public class MinStack2 {
    Stack<Integer> stack,minStack;//minStack就是辅助栈
    public MinStack2() {
        stack=new Stack<>();
        minStack=new Stack<>();
    }

    public void push(int x) {
        stack.push(x);
        if(minStack.empty()||minStack.peek()>=x){
            minStack.push(x);
        }
    }

    public void pop() {
        if(stack.pop().equals(minStack.peek())){
            minStack.pop();
        }
    }

    public int top() {
        return stack.peek();
    }

    public int getMin() {
        return minStack.peek();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java数据结构中的是一种具有特殊限制的线性数据结构。它是一种遵循先进后出原则的容器,可以通过两个主要操作来实现它的功能:推入(Push)和弹出(Pop)。 在许多应用场景中都有很大的用途。以下是Java中的一些常见应用之一: 1. 方法调用和返回:当一个方法被调用时,相关的参数和局部变量被推入中,当方法执行完毕后,这些值将会从中弹出。这样做的好处是,每个方法都有自己的局部变量,并且它们之间不会互相干扰。 2. 表达式求值:在Java中,被广泛用于计算表达式的值。当我们计算一个表达式时,我们将操作数和运算符推入中。然后,我们根据运算符的优先级来弹出中的元素并执行相应的计算。 3. 括号匹配:在处理括号匹配问题中也非常有用。我们可以使用来存储遇到的每个左括号,并在遇到相应的右括号时弹出顶元素。如果最后为空,那么说明所有的括号都是匹配的。 4. 浏览器的前进和后退功能:浏览器的前进和后退功能可以使用两个来实现。当我们在浏览器中点击后退按钮时,当前页面的URL被弹出当前页面,并被推入前一页。当我们点击后退按钮时,前一页中的URL被弹出并推入当前页面中。 这只是Java中的一些应用之一。还有许多其他的应用,如编辑器的撤销功能、迷宫求解等。的简单性和效率使得它在许多编程问题中都能发挥重要作用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值