栈(Stack)
hello,今天给大家带来的是栈的相关知识,包含了栈的实现原理,栈的相关api以及简单实现,还有一道经典的利用栈的括号匹配、逆波兰表达式的题。如果有用的话,大家可以点赞关注不迷路哟!
栈的实现原理
首先先看栈的特性图:
栈是继承于Vcetor类的,而Vcetor类又继承于AbstractList抽象类。所以栈也是属于List表的一类的。
对于栈的继承实现,则其容量的也是需要实现变化的,那么栈的扩容机制是怎么实现的呢?返回到父类中进行寻找得到如下:
栈的扩容机制是:当栈内元素满的时候需要扩容时,会以内部的元素总量的2倍进行扩容。
不过栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。对于栈的操作也有:
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶。
具体压栈出栈如下图所示:
栈的常用api及简单实现
api | 实现功能 |
---|---|
Stack() | 实现一个空栈 |
E push(E e) | 将e入栈,返回E类型的元素 |
E pop() | 栈顶元素取出并返回 |
E peek() | 返回栈顶元素(不取出栈顶元素) |
int size() | 获取栈中存放元素的个数 |
boolean empty() | 检测栈是否为空 |
以下是java对于栈的简单实现:
构建一个数组来,同时构建一个空的int内类型的数据,插入的时候进行数据++的操作,后续根据栈的操作来用数组实现。详看下码:
public class MyStack<E> {
//构建一个数组来实现栈的操作
E[] array;
int size;
//创建一个空栈
public MyStack(){
//配置好容量,//后续如果需要扩容时会进行扩容
array=(E[]) new Object[3];
size=0;
}
//入栈操作
public void push(E e){
//确保栈内空间充足 调用是否扩容的方法
ensureCapacity();
//插入元素
array[size]=e;
size++;
}
//获取栈顶元素操作
public E peek(){
if (empty()){
throw new RuntimeException("栈为空");
}
return array[size-1];
}
//将栈顶元素取出并返回
public E pop(){
E e=peek();
//删除栈顶元素
array[size]=null;
size--;
return e;
}
//获取栈中存放元素个数
public int size(){
return size;
}
//判断栈是否为空
public boolean empty(){
return size==0;
}
//扩容
private void ensureCapacity(){
if (size==array.length){
//容量增加为当前的2倍
array= Arrays.copyOf(array,size*2);
}
}
}
利用栈的实现括号匹配问题
这是力扣上的一道题以下为链接
题目描述:
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
解题思路:
将字符串进行判断数据长度,如果为0或者1直接不符合要求,直接判断false;然后将字符串进行遍历,如果要字符串有效的时必须形成括号匹配。所以采用栈的来进行存储,如果和设置号匹配条件进行入栈和出栈就可实现括号匹配,最后根据栈是否为空就可判断该字符串是否有效:
实现代码:
class Solution {
public boolean isValid(String s) {
//构建一个栈来接受存储的字符
Stack<Character> stack=new Stack<Character>();
if(s.length()==0||s.length()==1){
return false;
}
//遍历每一个字符
for(int i=0; i<s.length();i++){
char m = s.charAt(i);
if(m=='('||m=='['||m=='{'){
stack.push(m);
}else{
if(stack.empty()){
return false;
}else{
//就是碰到')','}',']'
//获取栈顶的元素
char c=stack.peek();
if((c=='('&&m==')')||(c=='['&&m==']')||(c=='{'&&m=='}')){
stack.pop();
}else{
return false;
}
}
}
}
if(!stack.empty()){
return false;
}
return true;
}
}
当然还有更高级的解法
思路:如果必须要实现 ( ),[ ] ,{ } 的字符串才有效,那么直接将所有的 ( ),[ ] ,{ } 替换为空字符串,最后判断长度是否为空即可实现。
代码实现如下:
class Solution {
public boolean isValid(String s) {
//这采用一半的原因是一次替换掉两个元素,不修改长度会产生越界。
int length = s.length() / 2;
for (int i = 0; i < length; i++) {
s = s.replace("()", "").replace("{}", "").replace("[]", "");
}
return s.length() == 0;
}
}片
最后还有一个逆波兰表达式的用栈解决的题:
逆波兰表达式就是遇到 + - * / 这四个数学运算符号时只需要将前面的两个数字实现用遇到的这个数学符号实现数学算法即可
举个例子 “2”,“1”,“+”,“3”,“*”
计算为: (2+1)*3
解题思路:遍历数组,将数字存放在栈的结构中,遇到运算符号拿出来运算,然后再放回栈顶,继续下一组运算,最后输出栈顶元素即可。
实现代码:
class Solution {
public int evalRPN(String[] tokens) {
//构建一个栈来实现
Stack<Integer> stack=new Stack<>();
int ret =0;
for (String s :tokens){
if (!(s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/"))){
stack.push(Integer.valueOf(s));
}else {
int m1= stack.pop();
int m2= stack.pop();
if (s.equals("+")){
ret=m2+m1;
}else if (s.equals("-")){
ret=m2-m1;
}else if (s.equals("*")){
ret=m2*m1;
}else if (s.equals("/")){
ret=m2/m1;
}
//需要将ret放入栈中
stack.push(ret);
}
}
return stack.pop();
}
}
今天栈的内容就分享到这里了,如果觉得有用的话就点个小小的赞吧!
在这里祝各位小伙伴中秋节快乐啦!!!