栈
栈的压入与弹出
1、描述: 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
2、思路: 遍历popped
数组,当popped[i]
等于当前栈中的栈顶元素时,说明此时需要出栈。是用if
还是while
循环?如果此时popped = [4,3,2,1,5]
,弹出前一个时,后一个仍然满足弹出条件,所以要使用while
循环。还有一个问题,程序如何判断popped
数组是不是合理的数组?当将pushed
数组遍历完毕,所有元素均入栈之后,最终在根据popped
数组判断是否要将栈顶元素弹出时,如果最终栈非null
,则对应的popped
数组一定不匹配。
3、代码实现
public class Offer31_IsPopOrder {
public boolean IsPopOrder(int [] pushA,int [] popA) {
//标识当前popA数组处理到那个元素
int i = 0;
Stack<Integer> stack = new Stack<>();
//将pushA数组中的元素依次入栈
for (int val: pushA) {
stack.push(val);
while (!stack.isEmpty() && stack.peek() == popA[i]){
//当栈顶的值和popA中的值相等,弹出
stack.pop();
i++;
}
}
//遍历完整个数组还没弹空,则非法
return stack.isEmpty();
}
}
有效的括号
1、描述:
2、思路: 用栈解决这个问题主要有点:只有碰到左括号才入栈;只要碰到右括号,在栈为非null的前提下,弹出栈顶元素,判断是否有匹配的左括号。
3、代码实现
public class Num20_IsValid {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(c == '(' || c == '{' || c == '['){
//碰到左括号直接入栈
stack.push(c);
}else{
//此时c为右括号,没有对应的左括号让他闭合
if(stack.isEmpty()){
//找到反例
return false;
}
char top = stack.pop();
if(c == ')' && top!='('){
return false;
}
if(c == '}' && top!='{'){
return false;
}
if(c == ']' && top!='['){
return false;
}
}
}
//此时没有相应的右括号让他闭合
return stack.isEmpty();
}
}
最小栈
1、描述:
2、思路: 这个题目的核心是getMin
方法的实现,要求要在常数时间内检索到最小元素,所以需要想一个办法将最小值每次都放在栈顶,但是还不能影响top
方法。我们采用双栈思路,引入另一个栈s2
,每次存放当前s1
栈中的最小值,当调用getMin
方法时,我们从s2
中获取,其他的方法在s1
上操作。
3、代码实现
public class MinStack {
private Stack<Integer> s1;
private Stack<Integer> s2;
public MinStack() {
s1 = new Stack<>();
s2 = new Stack<>();
}
//双栈
public void push(int val) {
//先将val压入s1
s1.push(val);
//如果s2为空,将同样的元素压入s2
if(s2.isEmpty()){
s2.push(val);
}else {
//此时s2不为空,先获取s2顶部的元素
int tempMin = s2.peek();
//比较要压入的元素与s2顶部元素的大小
//在s2中压入较小的元素,使其和s1中元素个数保持一致
if(val<tempMin){
s2.push(val);
}else{
s2.push(tempMin);
}
}
}
public void pop() {
s1.pop();
s2.pop();
}
public int top() {
return s1.peek();
}
public int getMin() {
return s2.peek();
}
}
逆波兰表达式求值
1、描述:
2、思路: 要做这道题,首先需要知道什么是逆波兰表达式。百度百科的定义:
同样借助栈,扫描整个字符串,当碰到数字时,将数字压入栈中,一旦碰到运算符,将栈中元素连弹两次,先弹出的是除数/减数,后弹出的是被除数/被减数,然后将运算结果继续压入栈中,最后扫描完整个字符串,此时栈顶元素就是最终的i计算结果。
3、代码实现
public class Num150_EvalRPN {
public int evalRPN(String[] tokens) {
Stack<Integer> s1 = new Stack<>();
//遍历整个字符串,判断是数字就压入
for ( String str: tokens) {
if(isNum(str)){
//将字符串转成数字,碰到数字就压入栈
s1.push(Integer.parseInt(str));
}else{
//此时不数字,碰到的是运算符
//从栈中连弹两个
//最先弹出的是被除数或被减数
int num1 = s1.pop();
//后弹出的是除数或减数
int num2 = s1.pop();
//计算
int result = 0;
switch (str){
case "+" :
result = num2 + num1;
break;
case "-" :
result = num2 - num1;
break;
case "*" :
result = num2 * num1;
break;
default:
result = num2 / num1;
}
//把计算结果再塞入栈中
s1.push(result);
}
}
//返回栈顶元素
return s1.peek();
}
//辨别是字符还是数字
private boolean isNum(String str){
try {
//将字符串转换为数字
Integer.parseInt(str);
//没报错返回true
return true;
}catch (NumberFormatException e){
return false;
}
}
}
队列
双队列实现栈
1、描述:
2、思路:
(1)新元素先入q2
;
(2)交换q1
和q2
引用,使得q1
永远是存储元素的队列;
(3)新元素再入q2
之后,将q1
中的元素依次出队再入q2
;
(4)交换q1
和q2
引用,重复以上步骤。
3、代码实现
public class Num225_MystackByTwoQueue {
//定义两个队列
//q1永远是储存元素的队列
private Queue<Integer> q1 = new LinkedList<>();
//q2永远保存新元素
private Queue<Integer> q2 = new LinkedList<>();
public void push(int x) {
//新元素入q2;
q2.offer(x);
//将q1的所以元素依次出队再入q2
while (!q1.isEmpty()){
q2.offer(q1.poll());
}
//交换q1和q2
Queue<Integer> tmp = q1;
q1 = q2;
q2 = tmp;
}
public int pop() {
return q1.poll();
}
public int top() {
return q1.peek();
}
public boolean empty() {
return q1.isEmpty();
}
}
一个队列实现栈
1、思路:
2、代码实现
public class Num225_MystackByOneQueue {
private Queue<Integer> queue = new LinkedList<>();
public void push(int x) {
//暂存一下当前队列中的元素个数
int size = queue.size();
//新元素入队
queue.offer(x);
//此时旧元素先出队再入队
for (int i = 0; i < size; i++) {
queue.offer(queue.poll());
}
}
public int pop() {
return queue.poll();
}
public int top() {
return queue.peek();
}
public boolean empty() {
return queue.isEmpty();
}
}
双栈实现队列
1、描述:
2、思路: 在队列中,新元素在队尾,所以类比到栈中,要让新元素在栈底而不是栈顶。在空栈中插入一定是栈底~。
3、代码实现
public class Num232_MyqueueByTwoStack {
//s1永远是存储元素的栈
private Stack<Integer> s1 = new Stack<>();
private Stack<Integer> s2 = new Stack<>();
public void push(int x) {
//先将s1中的所有元素依次压入S2中
while (!s1.isEmpty()){
s2.push(s1.pop());
}
//此时s1空出,栈底存放新元素,就是队尾
s1.push(x);
//再将s2中的所有元素依次弹出压入s1中
while (!s2.isEmpty()){
s1.push(s2.pop());
}
}
public int pop() {
return s1.pop();
}
public int peek() {
return s1.peek();
}
public boolean empty() {
return s1.isEmpty();
}
}
继续加油努力!!!