文章目录
括号匹配(LC20)
题目描述
给定一个只包括 (
,)
,{
,}
,[
,]
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s =()
输出:true
示例 2:
输入:s =()[]{}
输出:true
示例 3:
输入:s =(]
输出:false
示例 4:
输入:s =([])
输出:true
示例 5:
输入:s =([)]
输出:false
解题思路
字符串大致可以分为四种情况:
- 括号匹配,正确
- 左右括号不匹配
- 匹配但左括号多
- 匹配但右括号多
- 这种结构使用栈(先进后出)更合适,先定义一个栈,遍历字符串,只要字符是
[
{
(
其中一个,则入栈。 - 不是这种情况则先检查栈是否为空,如果为空则符合情况4,返回
false
- 如果栈不为空则获取栈顶元素,判断与当前字符是否匹配,匹配则删除,不匹配则返回
false
- 遍历结束后再检查栈是否为空,如果不为空,则复合第3种情况,返回
false
- 以上情况全部排除,即在括号匹配,字符串遍历完成且栈为空,则返回
true
代码示例
public boolean isValid(String s) {
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.isEmpty())
return false;
char ch1 = stack.peek();
if(ch1=='('&&ch==')'||ch1=='['&&ch==']'||ch1=='{'&&ch=='}')
stack.pop();
else
return false;
}
}
if(!stack.isEmpty())
return false;
return true;
}
逆波兰表达式(LC150)
题目描述
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
+
、-
、*
和/
。 - 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
示例 1:
输入:tokens =["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens =["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入:tokens =["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
解题思路
- 先判断当前字符是数字还是操作符,如果是数字则用
Integer.parseInt
转化为整型,压栈。 - 如果是操作符则拿出栈后两个元素,需要注意两个操作数的顺序。计算后再压栈
- 返回栈中最后的元素
代码示例
boolean isOperator(String s){
return (s.equals("+")||s.equals("-")||s.equals("*")||s.equals("/"));
}
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for(String str:tokens){
if(!isOperator(str)){
int x = Integer.parseInt(str);
stack.push(x);
}else{
int val1 = stack.pop();
int val2 = stack.pop();
switch(str){
case "+":
stack.push(val2+val1);
break;
case "-":
stack.push(val2-val1);
break;
case "*":
stack.push(val2*val1);
break;
case "/":
stack.push(val2/val1);
break;
}
}
}
return stack.pop();
}
出栈入栈次序匹配(JZ31)
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。
- 0 <=
pushV.length
==popV.length
<=1000 - -1000<=pushV[i]<=1000
- pushV 的所有数字均不相同
示例1
输入:[1,2,3,4,5],[4,5,3,2,1]
返回值:true
说明:可以通过push(1)=>push(2)=>push(3)=>push(4)=>pop()=>push(5)=>pop()=>pop()=>pop()=>pop()
这样的顺序得到[4,5,3,2,1]这个序列,返回true
示例2
输入:[1,2,3,4,5],[4,3,5,1,2]
返回值:false
说明:由于是[1,2,3,4,5]
的压入顺序,[4,3,5,1,2]
的弹出顺序,要求4,3,5必须在1,2前压入,且1,2不能弹出,但是这样压入的顺序,1又不能在2之前弹出,所以无法形成的,返回false
解题思路
- 创建栈来存放
pushA
中的元素,i
指针遍历pushV
中的元素,j
指针遍历popV
中元素 - 每存放一个元素,将栈顶元素与
popV[j]
对比,相等则出栈,直到栈空或不相等 - 最后如果栈空则返回
true
,不空返回false
代码示例
public boolean IsPopOrder (int[] pushV, int[] popV) {
if(pushV.length==0)
return false;
int j = 0;
Stack<Integer> stack = new Stack<>();
for(int i = 0;i<pushV.length;i++){
stack.push(pushV[i]);
while(!stack.isEmpty()&&stack.peek()==popV[j]){
stack.pop();
j++;
}
}
return stack.isEmpty();
}
最小栈问题(LC155)
题目描述
设计一个支持 push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack
类:
MinStack()
初始化堆栈对象。void push(int val)
将元素val推入堆栈。void pop()
删除堆栈顶部的元素。int top()
获取堆栈顶部的元素。int getMin()
获取堆栈中的最小元素。
示例 :
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
输出:[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
解题思路
创建一个栈stack
用于存放所有元素,一个栈minStack
存放最小元素
-
void push(int val)
将元素val推入stack。如果minStack
为空或者val<=
栈顶元素,则压栈。需要注意的是:相等也需要压入栈,因为后续有弹出的操作,所以stack
和minStack
中最小元素必须相等 -
void pop()
删除堆栈顶部的元素。如果stack
和minStack
栈顶元素相等,则minStack也删除 -
int getMin()
获取minStack
栈顶元素
代码示例
class MinStack {
Stack<Integer> stack = new Stack<>();
Stack<Integer> minStack = new Stack<>();
public MinStack() {
}
public void push(int val) {
stack.push(val);
if(minStack.isEmpty()||minStack.peek()>=val){
minStack.push(val);
}
}
public void pop() {
int val = stack.pop();
if(minStack.peek()==val)
minStack.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
设计循环队列(LC622)
题目描述
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k)
: 构造器,设置队列长度为 k 。Front
: 从队首获取元素。如果队列为空,返回 -1 。Rear
: 获取队尾元素。如果队列为空,返回 -1 。enQueue(value)
: 向循环队列插入一个元素。如果成功插入则返回真。deQueue()
: 从循环队列中删除一个元素。如果成功删除则返回真。isEmpty()
: 检查循环队列是否为空。isFull()
: 检查循环队列是否已满。
示例:
MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
circularQueue.enQueue(1); // 返回 true
circularQueue.enQueue(2); // 返回 true
circularQueue.enQueue(3); // 返回 true
circularQueue.enQueue(4); // 返回 false,队列已满
circularQueue.Rear(); // 返回 3
circularQueue.isFull(); // 返回 true
circularQueue.deQueue(); // 返回 true
circularQueue.enQueue(4); // 返回 true
circularQueue.Rear(); // 返回 4
解题思路
- 底层是数组
elem
,rear
用于维护队尾,front
用于维护队首,size
记录已存放元素的个数,capacity
记录数组容量。 - 数组的头和尾要特殊处理:
enQueue()
入队:考虑rear
是否在数组最后,如果是,则置为0;不是则rear+1
deQueue()
出队:与入队相同,考虑front
是否在数组最后,如果是,则置为0;不是则rear+1
Rear()
返回队尾元素:考虑rear
是否为0,是则访问最后一个下标,也就是capacity-1
;不是则访问rear-1
代码示例
class MyCircularQueue {
int[] elem;
int rear;
int front;
int size;
int capacity;
public MyCircularQueue(int k) {
this.elem = new int[k];
this.capacity = k;
}
public boolean enQueue(int value) {
if(isFull())
return false;
elem[rear] = value;
rear = (rear==capacity-1) ? 0 : rear+1;
size++;
return true;
}
public boolean deQueue() {
if(isEmpty())
return false;
front = (front==capacity-1) ? 0 : front+1;
size--;
return true;
}
public int Front() {
if(isEmpty())
return -1;
return elem[front];
}
public int Rear() {
if(isEmpty())
return -1;
int ret = rear==0 ? capacity-1 : rear-1;
return elem[ret];
}
public boolean isEmpty() {
return size==0;
}
public boolean isFull() {
return size==capacity;
}
}
用队列实现栈(LC225)
题目描述
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack
类:
void push(int x)
将元素 x 压入栈顶。int pop()
移除并返回栈顶元素。int top()
返回栈顶元素。boolean empty()
如果栈是空的,返回 true ;否则,返回 false 。
注意:
你只能使用队列的标准操作 —— 也就是 push to back
、peek/pop from front
、size
和 is empty
这些操作。
你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
示例:
输入:
[“MyStack”, “push”, “push”, “top”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]
解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False
解题思路
队列只能遵循先进先出,要实现栈就要用两个队列(q1
,q2
)搭配。
void push()
找到空的队列,依次入队。int pop()
先把队尾之前的元素依次入队到另一个空队列,最后把队尾元素弹出并返回int top()
与pop()
方法类似,所有元素依次入队到另一个空队列,但是需要一个变量(val
)记录出队的元素,最后返回val
boolean empty()
如果q1
,q1
都为空,则栈为空
代码示例
import java.util.LinkedList;
import java.util.Queue;
public class MyStackUseQueue {
Queue<Integer> q1 ;
Queue<Integer> q2 ;
public MyStackUseQueue() {
q1 = new LinkedList<>();
q2 = new LinkedList<>();
}
public void push(int x) {
if(!q1.isEmpty())
q1.offer(x);
else if(!q2.isEmpty())
q2.offer(x);
else
q1.offer(x);
}
public int pop() {
if(empty())
return -1;
if(q2.isEmpty()){
int size = q1.size();
for(int i = 0; i<size - 1;i++)
q2.offer(q1.poll());
int ret = q1.poll();
return ret;
}else{
int size = q2.size();
for(int i = 0; i<size - 1;i++)
q1.offer(q2.poll());
int ret = q2.poll();
return ret;
}
}
public int top() {
if(empty())
return -1;
if(q2.isEmpty()){
int val = 0;
int size = q1.size();
for(int i = 0; i<size;i++){
val = q1.poll();
q2.offer(val);
}
return val;
}else{
int val = 0;
int size = q2.size();
for(int i = 0; i<size;i++){
val = q2.poll();
q1.offer(val);
}
return val;
}
}
public boolean empty() {
return q1.isEmpty() && q2.isEmpty();
}
}
用栈实现队列(LC232)
题目描述
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素boolean empty()
如果队列为空,返回 true ;否则,返回 false
说明:
你 只能 使用标准的栈操作 —— 也就是只有 push to top
, peek/pop from top
, size
, 和 is empty
操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list
或者 deque
(双端队列)来模拟一个栈,只要是标准的栈操作即可。
示例 1:
输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:[null, null, null, 1, 1, false]
解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false
解题思路
与上一题类似,用两个栈来实现(s1
,s2
)队列。指定s1用于入队,s2用于出队
void push(int x)
将元素 x 压栈到s1
中int pop()
返回s2
栈顶元素,如果s2
为空,则先将s1
的所有元素依次压栈到s2
中,再返回s2
栈顶元素int peek()
与pop()
类似,最后将s2栈顶元素弹出并返回boolean empty()
如果s1
,s2
都为空则返回true
代码示例
import java.util.Stack;
public class MyQueueUseStack {
Stack<Integer> s1;
Stack<Integer> s2;
public MyQueueUseStack() {
s1 = new Stack<>();
s2 = new Stack<>();
}
public void push(int x) {
s1.push(x);
}
public int pop() {
if(empty())
return -1;
if(s2.isEmpty())
while(!s1.isEmpty())
s2.push(s1.pop());
return s2.pop();
}
public int peek() {
if(empty())
return -1;
if(s2.isEmpty())
while(!s1.isEmpty())
s2.push(s1.pop());
return s2.peek();
}
public boolean empty() {
return s1.isEmpty() && s2.isEmpty();
}
}