栈概念 转化为循环 括号匹配 逆波兰表达式 模拟实现 干货满满

1.栈的概念及使用

1.1栈的概念

栈:是一种特殊的线性表,值允许在固定的一端进行插入和删除元素的操作。进行数据插入和删除的一端称为栈顶,另一端称为栈底。后进先出
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
出栈:栈的删除操作叫做。出数据在栈顶
在这里插入图片描述

大神形容:吃进来吐出去。哈哈哈哈哈哈哈哈哈哈哈。也是很形象了。

1.2栈的使用

1.2.1常用方法

Stack是Vector的一个子类,它实现标准的后进先出堆栈。
Stack 仅仅定义了创建空堆栈的默认构造函数。
Stack包括了由Vector定义的所有方法,同时增加了几种它自己定义的方法,介绍如下:
boolean empty( )-——-如果堆栈是空的,则返回true,当堆栈包含元素时,返回false。
Object peek( )———–返回位于栈顶的元素,但是并不在堆栈中删除它。
Object pop( )————返回位于栈顶的元素,并在进程中删除它。
Object push (Object element )———将element压入堆栈,同时也返回element。
int search(Object element)———在堆栈中搜索element,如果发现了,则返回它相对于栈顶的偏移量。否则,返回-1。

boolean empty():为空返回true,否则返回false

public static void main(String[] args){
Stack<Integer> s = new Stack();
s.push(1);
s.push(2);
s.push(3);
s.push(4);
System.out.println(s.size());  //获取栈中有效元素
System.out.println(s.peek());  // 获取栈顶元素-----4
s.pop(); //4出栈,栈中剩余1  2  3 ,栈顶元素剩余 1 2  栈顶元素为3
System.out.println(s.pop());
if(s.empty()){   //判空
System.out.println(s.size());
}
}

1.3栈的应用场景

1.3.1改变元素的序列

例题:

(例)设栈的入栈序列是 1 2 3 4,则下列不可能是其出栈序列的是( )。
A. 1 2 4 3
B. 2 1 3 4
C. 1 4 3 2
D. 4 3 1 2
E. 3 2 1 4
可以将各个选项都模拟一遍选出正确答案
这当然可以得出正确的答案 (D )

1.3.2将递归转化为循环

//递归方式
void printlist(Node node){
if(null != node){
printlist(node.next);
system.out.println(node.val + "");
}
}
//循环方式
void printlist(Node node){
if(null == node){
return;
}
Stack<Node> s = new Stack<>();
//将链表中的结点保存在栈中
Node cyr - node;
while(null != node){
s.push(cur);
cur = cur.next;
}

为什么可以使用栈降低贵转换为循环而不是其他结构?
后调的先推出,先调用的后退出—>方法之间的递归,调用的次序刚好和栈的特性是匹配的。并不是所有的递归转化成循环都需要用到栈,有些递归方法循环时不需要(求阶乘,斐波那契数列)

1.3.3 括号匹配

在这里插入图片描述
解题思路: 对字符串进行遍历,依次拿到其中每个元素(括号)
左括号: push
与栈顶元素进行比对 必须确保栈中有元素
匹配: 将栈顶元素出栈
不匹配: 直接返回 false

package Day1016;

import java.util.Stack;

public class KuoHao {
    public boolean isVaild(String s){
        Stack<Character> s = new Stack<>();
        for (int i = 0;i<s.length();++i){
            //检测第i个括号是左括号还是右括号
             char c = s.charAt(i);
             if(c=='(' || c=='['||c=='{'){
                 //当前括号为左括号,将其入栈
                 s.push();
             }
             //当前括号为右括号,将其与栈顶元素比较
             else {
                 if (s.empty()) {
                     return false;
                 }
                 //获取栈顶的左括号
                 char top = s.peek();  //注意:peek只是将栈顶元素获取到,并不会将栈顶元素删除掉
                 if ((top == '(' && c == ')')||
                 (top == '[' && c == ']') || (top == '{' && c == '}') ){
                     s.pop();
                     continue;
                 }
             else{
                     return false;
                 }
             }
                 
             }
        return true;
        }
    }


1.3.4逆波兰表达式

解题思路:

逆波兰表达式又叫做后缀表达式。逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・Lukasiewicz)于1929年首先提出的一种表达式的表示方法 [1] 。后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。

依次遍历表达式中的每一项
如果该项是数字---->入栈
如果该项是运算符,直接冲栈顶去掉两个元素进行该种运算,将结果入栈

表达式最终计算的结果在栈顶

package NiuKeTI;

import java.util.Stack;

class NiBoLan {
     Stack<Integer> s = new Stack<>();
     for(String e){
         if(!((e .equals("+"))||(e .equals("-"))||(e .equals("*"))||(e .equals("/")))){
             s.push(Integer.parseInt(e));

         }else
         {
             //e是运算符
             //从栈顶获取两个操作数进行运算
             int right = s.pop();
             int left = s.pop();
             switch(ch){
                 case'+':
                     s.push(left+right);
                     break;
                 case'-':
                     s.push(left-right);
                     break;
                 case'*':
                     s.push(left*right);
                     break;
                 case'/':
                     s.push(left/right);
                     break;
                 default:
                     break;
             }
         }


    }
}

1.3.5 栈的压入,弹出序列

解题思路:检测入栈和出栈的合理性
input:1 2 3 4 5
out: 4 5 3 2 1
out: 4 3 5 1 2
/输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列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是该压栈序列对应的一个弹出序列,
//但4,3,5,1,2就不可能是该压栈序列的弹出序列。
//(注意:这两个序列的长度是相等的)
import java.util.ArrayList;

public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
          Stack <Integer> s = new Stack();
    int pushIdx = 0, outIdx = 0;
    while(outIdx < popA.length){
        //当栈空或者栈顶元素与待出元素不相等时候入栈
        while(s.empty() || s.peek() != popA[outIdx]){
            //有元素时,继续入栈
            if(pushIdx < pushA.length){
                s.push(pushIdx);
                pushIdx++;
            }
            else {
                return false;
            }
        }
        s.pop();
        outIdx++;
    }
    }
    
    
      
   

1.4 栈的模拟实现

Stack继承了Vector,Vector和arraylist类似,都是动态的顺序表,不同的是Vector是线程安全的。

思路:
1、使用链表实现一个栈,便于扩容(不用考虑数组扩容场景),维护根节点和最后入栈的节点
2、节点中维护pre指针,便于pop()时快速找到上一个节点

package com.datastructure.stackqueue;
 
/**
 * 实现一个栈,自定义栈,用链表实现,方便扩容 
 */
public class DefineStack<T> {
 
	private Node<T> root;
	
	private Node<T> tail;
	
	static class Node<T> {
		T value;
		Node<T> pre;
		Node (T value) {
			this.value = value;
		}
		
        @Override
        public String toString() {
            if (this.pre == null) {
                return String.valueOf(this.value);
            }
            return this.pre.toString() + " <- " + this.value;
        }
	} 
	
	/**
	 * 有前驱指针的节点
	 * 
	 * root <- tail
	 * root.pre = null
	 * tail.pre = root
	 *  
	 */
	public T push(T t) {
		if (root == null) {
			root = new Node<T>(t);
			tail = root;
			return t;
		}
		Node<T> node = new Node<T>(t);
		node.pre = tail;
		tail = node;
		return t;
	}
	
	public T pop() {
		if (null == root) {
			throw new RuntimeException("stack is empty");
		}
		
		T ret = null;
		if (root == tail) {
			ret = root.value;
			root = null;
			tail = null;
			return ret;
		}
		ret = tail.value;
		tail = tail.pre;
		return ret;
	}
	
	@Override
	public String toString() {
		return tail.toString();
	}
	
	public static void main(String[] args) {
		DefineStack<Integer> s = new DefineStack<>();
		s.push(1);
		s.push(2);
		s.push(3);
		s.pop();
		s.push(4);
		s.push(5);
		s.pop();
		System.out.println(s);
	}
 
}

1.5 栈,虚拟机栈。栈帧有什么区别?

栈帧:一种结构,这种结构与函数调用相关,内部:局部变量表,操作数栈。每个方法在运行=时,jvm都会创建一个栈帧,然后将栈帧压入到虚拟机栈中,当方法调用结束时,该方法对应的栈帧会从虚拟机中出栈
注意每个方法中的栈帧结构都是一样的,大小可能不同
栈:后进先出的一种数据结构,在java集合中有对应的实现–stack。stack在实现中继承了vector
虚拟机栈:具有特殊作用的一块内存空间

jvm为了对数据进行更好的管理,将内存按照不同的需求划分出来的一种结构 栈区 堆区
栈区:线程是私有的,栈区中存放的是函数调用相关一些信息,主要是栈帧
当栈区中内存空间不足时,会抛出StackOverFLowException
当中的元素(栈帧)是按照数据结构中栈的特性来实现的

  • 13
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值