用栈实现队列
栈的顺序为后进先出,而队列的顺序为先进先出。使用两个栈实现队列,一个元素需要经过两个栈才能出队列,在经过第一个栈时元素顺序被反转,经过第二个栈时再次被反转,此时就是先进先出顺序。
class MyQueue {
private Stack<Integer> in;
private Stack<Integer> out;
/** Initialize your data structure here. */
public MyQueue() {
in = new Stack<>();
out = new Stack<>();
}
/** Push element x to the back of queue. */
public void push(int x) {
in.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
inToOut();
return out.pop();
}
/** Get the front element. */
public int peek() {
inToOut();
return out.peek();
}
public void inToOut(){
if(out.isEmpty()){
while(!in.isEmpty()){
out.push(in.pop());
}
}
}
/** Returns whether the queue is empty. */
public boolean empty() {
return in.isEmpty() && out.isEmpty();
}
}
用队列实现栈
leetcode225
在将一个元素 x 插入队列时,为了维护原来的后进先出顺序,需要让 x 插入队列首部。而队列的默认插入顺序是队列尾部,因此在将 x 插入队列尾部之后,需要让除了 x 之外的所有元素出队列,再入队列。
例如:
[2,1]->[1,2]
[3,1,2]->[1,2,3]
class MyStack {
Queue<Integer> queue;
/** Initialize your data structure here. */
public MyStack() {
queue = new LinkedList<>();
}
/** Push element x onto stack. */
public void push(int x) {
queue.add(x);
int size = queue.size();
while(size-- > 1){
queue.add(queue.remove());
}
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return queue.poll();
}
/** Get the top element. */
public int top() {
return queue.peek();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return queue.size() == 0;
}
}
class MyStack:
def __init__(self):
"""
Initialize your data structure here.
"""
self.queue = []
def push(self, x: int) -> None:
"""
Push element x onto stack.
"""
self.queue.append(x)
for i in range(len(self.queue)):
self.queue.append(self.queue.pop(0))
def pop(self) -> int:
"""
Removes the element on top of the stack and returns that element.
"""
return self.queue.pop()
def top(self) -> int:
"""
Get the top element.
"""
return self.queue[-1]
def empty(self) -> bool:
"""
Returns whether the stack is empty.
"""
return not self.queue
最小值栈
leetcode155
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
- push(x) —— 将元素 x 推入栈中。
- pop() —— 删除栈顶的元素。
- top() —— 获取栈顶元素。
- getMin() —— 检索栈中的最小元素。
法一:直接底层构造MinStack
class MinStack {
private Node head;
private class Node{
int val;
int min;
Node next;
public Node(int val, int min) {
this.val = val;
this.min = min;
next = null;
}
}
public void push(int x) {
if (head == null) {
head = new Node(x, x);
}else {
Node node = new Node(x, Math.min(x, head.min));
node.next = head;
head = node;
}
}
public void pop() {
head = head.next;
}
public int top() {
return head.val;
}
public int getMin() {
return head.min;
}
}
法二:双栈实现,一个存储值,一个存储最小值
class MinStack {
private Stack<Integer> dataStack;
private Stack<Integer> minStack;
private int min;
public MinStack(){
dataStack = new Stack<>();
minStack = new Stack<>();
min = Integer.MAX_VALUE;
}
public void push(int x) {
dataStack.push(x);
min = Math.min(x, min);
minStack.push(min);
}
public void pop() {
dataStack.pop();
minStack.pop();
min = minStack.isEmpty() ? Integer.MAX_VALUE : minStack.peek();
}
public int top() {
return dataStack.peek();
}
public int getMin() {
return minStack.peek();
}
}
法三:用一个栈,但更新最小值时把原最小值再入栈一次
class MinStack {
int min = Integer.MAX_VALUE;
Stack<Integer> stack = new Stack<Integer>();
public void push(int x) {
//当前值更小
if(x <= min){
//将之前的最小值保存
stack.push(min);
//更新最小值
min=x;
}
stack.push(x);
}
public void pop() {
//如果弹出的值是最小值,那么将下一个元素更新为最小值
//这里进入if判断语句,已经先执行一次pop()
if(stack.pop() == min) {
min=stack.pop();
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return min;
}
}
法四:压入差值
同样是用一个 min 变量保存最小值。只不过栈里边我们不去保存原来的值,而是去存储入栈的值和最小值的差值。然后得到之前的最小值的话,我们就可以通过 min 值和栈顶元素得到,举个例子。
入栈 3,存入 3 - 3 = 0
| | min = 3
| |
|_0_|
stack
入栈 5,存入 5 - 3 = 2
| | min = 3
| 2 |
|_0_|
stack
入栈 2,因为出现了更小的数,所以我们会存入一个负数,这里很关键
也就是存入 2 - 3 = -1, 并且更新 min = 2
对于之前的 min 值 3, 我们只需要用更新后的 min - 栈顶元素 (-1) 就可以得到
| -1| min = 2
| 5 |
|_3_|
stack
入栈 6,存入 6 - 2 = 4
| 4 | min = 2
| -1|
| 5 |
|_3_|
stack
出栈,返回的值就是栈顶元素 4 加上 min,就是 6
| | min = 2
| -1|
| 5 |
|_3_|
stack
出栈,此时栈顶元素是负数,说明之前对 min 值进行了更新。
入栈元素 - min = 栈顶元素,入栈元素其实就是当前的 min 值 2
所以更新前的 min 就等于入栈元素 2 - 栈顶元素(-1) = 3
| | min = 3
| 5 |
|_3_|
stack
public class MinStack {
long min;
Stack<Long> stack;
public MinStack(){
stack=new Stack<>();
}
public void push(int x) {
if (stack.isEmpty()) {
min = x;
stack.push(x - min);
} else {
stack.push(x - min);
if (x < min){
min = x; // 更新最小值
}
}
}
public void pop() {
if (stack.isEmpty())
return;
//由于我们保存的是差值,所以可能造成溢出,所以我们用了数据范围更大的 long 类型。
long pop = stack.pop();
//弹出的是负值,要更新 min
if (pop < 0) {
min = min - pop;
}
}
//???个人认为只要返回栈顶元素即可,min更新会在pop时,但提交错误
public int top() {
long top = stack.peek();
//负数的话,出栈的值保存在 min 中
if (top < 0) {
return (int) (min);
//出栈元素加上最小值即可
} else {
return (int) (top + min);
}
}
public int getMin() {
return (int) min;
}
}
用栈实现括号匹配
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
- 法一
class Solution {
public boolean isValid(String s) {
//字符长度必须是偶数,空字符串也为true
if ((s.length() & 1)== 1){
return false;
}
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 {
//栈空,说明表达式无效,没有与此时右括号匹配的左括号
if (stack.isEmpty()){
return false;
}
if (c == ')' && stack.peek() == '('){
stack.pop();
}
else if (c == '}' && stack.peek() == '{'){
stack.pop();
}
else if (c == ']' && stack.peek() == '['){
stack.pop();
}else {
return false;
}
}
}
//栈不为空,false
return stack.isEmpty();
}
}
- 法二:官方解法,用hashmap
class Solution {
private Map<Character,Character> map;
public Solution(){
map = new HashMap<>();
this.map.put(')','(');
this.map.put(']','[');
this.map.put('}','{');
}
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
if((s.length() & 1) == 1){
return false;
}
for(int i = 0; i < s.length(); i++){
char c = s.charAt(i);
if(map.containsKey(c)){
char top = stack.isEmpty() ? '#' : stack.peek();
if(top == map.get(c)){
stack.pop();
}else{
return false;
}
}else{
stack.push(c);
}
}
return stack.isEmpty();
}
}
- 法三:最简(cyc大佬博客这里)
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for (char c : s.toCharArray()) {
if (c == '(' || c == '{' || c == '[') {
stack.push(c);
} else {
if (stack.isEmpty()) {
return false;
}
char cStack = stack.pop();
boolean b1 = c == ')' && cStack != '(';
boolean b2 = c == ']' && cStack != '[';
boolean b3 = c == '}' && cStack != '{';
//b1、b2、b3有一个true
if (b1 || b2 || b3) {
return false;
}
}
}
return stack.isEmpty();
}
数组中元素与下一个比它大元素之间的距离
- 法一:用栈保存数组元素
在遍历数组时用栈把数组中的数存起来,如果当前遍历的数比栈顶元素来的大,说明栈顶元素的下一个比它大的数就是当前元素。
从左往右遍历
class Solution {
public int[] dailyTemperatures(int[] T) {
int len = T.length;
int[] dist = new int[len];
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < len; i++) {
while (!stack.isEmpty() && T[i] > T[stack.peek()]) {
int preIndex = stack.pop();
dist[preIndex] = i - preIndex;
}
//stack本身没有add()方法,但是继承的类vector有add方法,同样vector的父类和实现接口List同样有add()方法。
stack.add(i);
}
return dist;
}
}
从右往左
class Solution {
public int[] dailyTemperatures(int[] T) {
int[] dist = new int[T.length];
Stack<Integer> s = new Stack<>();
for(int i = T.length-1; i >= 0; i--){
while(!s.empty() && T[i] >= T[s.peek()]){
s.pop();//将不大于当前元素的剔除
}
dist[i] = s.empty() ? 0 : (s.peek() - i);
s.push(i);
}
return dist;
}
}
- 法二:从数组右边遍历,减少重复次数
class Solution {
public int[] dailyTemperatures(int[] T) {
int N = T.length;
int[] dist = new int[N];
Arrays.fill(dist,0);
for(int i = N - 2; i >= 0; i--){
// j+= dist[j]是利用已经有的结果进行跳跃
for(int j = i + 1; j < N; j += dist[j]){
if(T[j] > T[i]){
dist[i] = j - i;
break;
}else if(dist[j] == 0){//遇到0表示后面不会有更大的值,那当然当前值就应该也为0
dist[i] = 0;
break;//不加的话会超出时间限制
}
}
}
return dist;
}
}
循环数组中比当前元素大的下一个元素
class Solution {
public int[] nextGreaterElements(int[] nums) {
int N = nums.length;
int[] result = new int[N];
Arrays.fill(result,-1);
Stack<Integer> s = new Stack<>();
for(int i = 0; i < 2*N; i++){
while(!s.isEmpty() && nums[i%N] > nums[s.peek()]){
int pre = s.pop();
result[pre] = nums[i%N];
}
if(i < N){
s.push(i);
}
}
return result;
}
}