栈的使用
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则
栈是先进先出的数据结构;怎么构思呢;很容易和队列记反。你自己想;队列;排队的队;排队坐高铁;先进去的人先走出到高铁站。栈是反着的。
import java.util.Stack;
public class stack {
public static void main(String[] args) {
Stack<Integer> stack=new Stack<>();
if(stack.empty()==true){//是否为空
System.out.println("是的为空");
}
stack.push(1);
stack.push(12);
stack.push(123);
stack.push(1234);
stack.push(12345);
int x=stack.pop();//取栈顶元素
System.out.println(x);
System.out.println(stack.peek());//瞄一眼栈顶元素
}
}
栈的实现
这个集合类底层是数组还是链表呢? 数组
import java.util.Arrays;
import java.util.Stack;
public class Mystack {
public int[] elem;
public int usedSize;
public static final int DEFAULT_SIZE = 10;
public Mystack() {
this.elem = new int[DEFAULT_SIZE];
}
public int push(int val) {
if(isFull()) {
elem = Arrays.copyOf(elem,2*elem.length);
}
this.elem[usedSize] = val;
usedSize++;
return val;
}
public boolean isFull() {
return usedSize == elem.length;
}
public int pop(){
if (usedSize==0){
System.out.println("栈为空;不能出栈;严谨一点应该报异常;避免元素值真的是负一");
return -1;
}
//返回了;还得给栈的长度减1
int ret=elem[usedSize--];
usedSize--;
return ret;//直接return [--usedSize];更高效
}
public boolean empty(){//有效元素是否为0
return usedSize==0;
}
public int peek(){
if (empty()){
System.out.println("栈为空");
return -1;
}
return elem[usedSize-1];
}
public static void main(String[] args) {
Mystack mystack=new Mystack();
mystack.push(100);
}
}
逆波兰式
假设:这有一个中缀表达式:a+bc+(de+f)g;如何转成后缀表达式
1:按从左到右加减乘除的优先级加括号:
2:先处理乘号变成:a+(bc)+( ((d *e)+f)g)
3:再处理加号变成:((a+(bc) )+( (((d *e)+f) )*g))
4:把这些符号挪到对应的括号后面(如果放前面就叫前缀表达式):挪完把括号去掉
后缀表达式求值
给你应该后缀表达式求结果;怎么求呢? 1 2 3*+456+7+
代码逻辑:
创建一个栈遍历这个字符串;如果是数字则放进栈里;遇到运算符则取出栈顶的两个元素;搭配这个符号;最顶的作为右操作数;其次的作为左操作数。先得到2 *3;计算的结果为6再放进栈里
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
// 只需要把数字传进去,当我们遇到数字的时候,利用字符串转数字的方法就好了
for(int i=0;i<tokens.length;i++){
String t=tokens[i];
//这里不能省略 String t=tokens[i];switch (tokens[i])。switch有传入类型要求,tokens[i]是string []类型
switch (t){
case "+":
stack.push(stack.pop() + stack.pop());//直接就取出来操作了,放进去,。
break;
case "-":
int num = stack.pop();
stack.push(stack.pop() - num);
break;
case "*":
stack.push(stack.pop() * stack.pop());
break;
case "/":
int num2 = stack.pop();
stack.push(stack.pop() / num2);
break;
default:
stack.push(Integer.valueOf(t));//数字字符串入栈,得转类型,如果是数字自动走到这行
}
}
return stack.pop();
}
}
递归打印链表转化为循环
递归的方式实现:你还可以反转链表呢
public void displayLink(ListNode head){
//就是递到尾再归。
ListNode cur=head;
if(cur!=null){
displayLink(cur.next);
System.out.print(cur.val);
}
}
改成非递归;循环去写,栈,存进栈里再一个一个取出来
public void display(ListNode head){
ListNode cur=head;
Stack<ListNode> stack=new Stack<>();
while (cur!=null){
stack.push(cur);
cur=cur.next;
}
while (!stack.empty()){
System.out.print(stack.pop().val);
}
}
括号匹配
字符串写法:这种方式确实很不错;如果括号是在一起的就能直接替换掉;如果是这种包含[ ( ) ];第一次[ ]就没法替换掉;所以需要循环;考虑最坏情况。所有的括号只有一对在一起。
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;
}
括号匹配
不能通过括号个数来判断的;顺序上有要求我们就用栈;正常情况一定是左右都有的
逻辑:因为异常情况是非常多;但是我们可以选择正确的情况;剩下就是异常的。只要是左括号往栈里放;如果遇到右括号那就取栈顶的匹配一下看看是否合适。(还有种特殊情况我们上面是没涉及到;栈为空有右括号);最后如果遍历完;栈里还不为空就说明左括号有多。
注意:它输入的字符串全是括号的那种;所以不是左括号就是右括号。这里ch不是左括号就是右括号;如果是左括号则执行if添加进栈里;如果是右括号则走else;去取栈顶瞄一眼(直接取都行啊;反正匹配的话你也要取元素;不匹配就直接返回false);对比一下是否匹配
public static boolean isValid(String s){
//两个方式;要么判断错误情况;要么判断正确情况。这题当然判断正确情况
// 就消消乐嘛;里面的先消;然后外面的就能消了 。[({ })]、[{} ()]
//思路很简单;左括号放栈里;遇到右括号取栈顶看看匹不匹配。
Stack<Character> stack=new Stack<>();
for (int i = 0; i <s.length() ; i++) {
if(s.charAt(i)=='('||s.charAt(i)=='['&&s.charAt(i)=='{'){
stack.push(s.charAt(i));
}else {
if(stack.empty()){
return false;
}
char ch=stack.pop();
if((ch=='('&&s.charAt(i)==')') ||(ch=='{'&&s.charAt(i)=='}') ||(ch=='['&&s.charAt(i)==']')){
continue;
}else {
return false;
}
}
}
if(!stack.empty()){//左括号有多的情况
return false;
}
return true;
}
出入次序匹配
判断出栈的顺序是否匹配题目
逻辑:
public class Solution1 {
public boolean IsPopOrder(int [] pushA, int [] popA) {
Stack<Integer> stack = new Stack<>();
int j = 0;
for (int i = 0; i < pushA.length ; i++) {
stack.push(pushA[i]);
while (!stack.empty()&&stack.peek() == popA[j]) {//瞄一眼相不相等,相等我再取出来
stack.pop();
j++;
}
}
return stack.empty();
}
}
最小栈
题目链接
设计一个支持 push ,pop,peek操作,并能在常数时间内检索到最小元素的栈。
其实很简单:就是普通的创建一个栈;但是你需要另一个栈存最小值
push:入栈你得考虑是不是最小的这个值;是最小就得一起放入最小栈。注意:相等的最小得一起放入(不然你不知道出这个值时;最小栈出还是不出)。
top没什么好说的;就是peek瞄一眼栈顶。
pop:你如果出的是最小值你得连最小栈的一起出。但是当使用最小值都出完;最小栈为null;你得考虑更新这个最小栈。
getMin:去最小栈栈顶瞄一眼即可
import java.util.Stack;
class MinStack {
private Stack<Integer> stack1;
private Stack<Integer> minStack;
public MinStack() {
stack1 = new Stack<>();
minStack = new Stack<>();
}
public void push(int val) {
stack1.push(val);
if(minStack.empty()) {
minStack.push(val);
}else {
int x = minStack.peek();
if(val <= x) {
minStack.push(val);
}
}
}
public void pop() {
if(!stack1.empty()) {
int x = stack1.pop();//在栈1弹出;如果是最小值;在最小栈也弹出
if(x == minStack.peek()) {
//虽然力扣能通过;这里有个bug但是你这样子下一次不就不能取最小值吗;minstack都没元素了。
//你得更新最小值啊;这时候最小栈是null吗;也不一定;可能还有其它相同的值最小值。你就判断如果是null就更新最小值;先把pop倒入最小栈里;然后再倒回去;这个过程记录最小值(用数组存;因为可能多个最小值是一样;得全部存进去)。最把这个放入最小值
minStack.pop();
}
}
}
//只针对 普通的栈来说的 获取普通栈的栈顶元素 相当于peek函数
public int top() {
if(!stack1.empty()) {
return stack1.peek();
}
return -1;//不要抛异常
}
public int getMin() {
if(minStack.empty()) {
return -1;
}
return minStack.peek();
}
}