我们都知道栈和队列是两种很相似的数据结构,但是又有他们的特点
-
栈是一种先进后出的数据结构,FILO(先进后出)
-
队列是一种先进先出的数据结构,FIFO(先进先出)
本文主要关注几道leetcode上关于栈的题目,进而进一步熟悉使用栈
有效的括号表达式
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 注意空字符串可被认为是有效字符串。
输入: "()"
输出: true
输入: "()[]{}"
输出: true
输入: "([)]"
输出: false
我们知道,栈的一大应用就是用来检查表达式或者编译器的语法正确性,栈是检查是否每件事情都能成一对的好工具
public class IsValid {
/**
* 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
*
* 有效字符串需满足:
*
* 左括号必须用相同类型的右括号闭合。
* 左括号必须以正确的顺序闭合。
* 注意空字符串可被认为是有效字符串。
*/
/**
* 判断括号正确性
* @param s
* @return
*/
public boolean isValid(String s) {
// 使用栈数据结构来解决这道题
Stack<Character> stack = new Stack<>();
// 将字符串转为字符数组,依次进栈
char[] charArray = s.toCharArray();
for(char c : charArray){
// 如果栈为空则,push进栈
// 如果栈不为空,则判断要进栈的元素和栈顶元素是否匹配
// 如果匹配则pop弹出栈顶元素
// 不匹配则继续进栈
// 最后判断栈是否为空,为空则表示全部匹配成功
if(stack.size()==0){
stack.push(c);
}else if(isMatch(stack.peek(),c)){
stack.pop();
}else{
stack.push(c);
}
}
return stack.size() == 0;
}
// 判断两个符号是否匹配
public boolean isMatch(char c1,char c2) {
if ((c1 == '(' && c2 == ')') || (c1 == '{' && c2 == '}') || (c1 == '[' && c2 == ']')) {
return true;
} else {
return false;
}
}
}
辅助栈
最小栈
实现一个最小栈,保证以线性时间从栈中返回栈中最小元素
要实现最小栈,关键就是如何构建栈,我们需要一个辅助栈,该辅助栈只存一个最小元素,一个是正常栈,存放每次压栈元素,所以,在正常栈的进栈的时候,要进行一个比较,即最小栈中栈顶元素和要插入的元素进行比较
public class MinStack {
/**
* <h>最小栈</h>
* <li>push(x) -- 将元素 x 推入栈中。</li>
* <li>pop() -- 删除栈顶的元素。</li>
* <li>top() -- 获取栈顶元素。</li>
* <li>getMin() -- 检索栈中的最小元素。</li>
*/
Stack<Integer> stack;
Stack<Integer> minStack;
/**
* initialize your data structure here.
*/
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
public void push(int x) {
stack.push(x);
// if (minStack.isEmpty() || x <= minStack.peek()) {
// minStack.push(x);
// }
if(minStack.isEmpty()){
minStack.push(x);
}else{
int min = minStack.pop();
if(min > x){
minStack.push(x);
}else{
minStack.push(min);
}
}
}
public void pop() {
if (stack.peek().equals(minStack.peek())) {
minStack.pop();
}
stack.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
最大栈
public class MaxStack {
/**
* <h>最大栈</h>
* <li>push(x) -- 将元素 x 推入栈中。</li>
* <li>pop() -- 删除栈顶的元素。</li>
* <li>top() -- 获取栈顶元素。</li>
* <li>getMax() -- 检索栈中的最大元素。</li>
*/
Stack<Integer> stack;
Stack<Integer> maxStack;
/**
* initialize your data structure here.
*/
public MaxStack() {
stack = new Stack<>();
maxStack = new Stack<>();
}
public void push(int x) {
stack.push(x);
//我希望维护的最大栈是只有一个栈元素的辅助栈
// if(maxStack.isEmpty() || maxStack.peek() <= x){
// maxStack.push(x);
// }
if(maxStack.isEmpty()){
maxStack.push(x);
}else{
int max = maxStack.pop();
if(max < x){
maxStack.push(x);
}else {
maxStack.push(max);
}
}
}
public void pop() {
if(stack.peek().equals(maxStack.peek())){
maxStack.pop();
}
stack.pop();
}
public int top() {
return stack.peek();
}
public int getMax() {
return maxStack.peek();
}
public boolean isEmpty(){
return stack.isEmpty();
}
public static void main(String[] args) {
MaxStack maxStack = new MaxStack();
maxStack.push(1);
maxStack.push(5);
maxStack.push(32);
maxStack.push(2);
maxStack.push(1);
maxStack.push(0);
System.out.println(maxStack.getMax());
}
}
栈实现队列
使用辅助栈实现队列也是一个不错的选择,栈是一个先进后出的结构,队列是一个先进先出的结构,我们能使用双栈实现,进栈还是正常的压入正常栈,但是在出栈的时候,要满足队列的先进先出的特性,所以,出栈出来的应该是栈底元素而不是栈顶元素,所以就需要用到辅助栈,在出栈时,将正常栈的元素一个一个从正常栈栈顶到栈底转移到辅助栈,当正常栈是空时,辅助栈栈顶元素就是刚刚正常栈的栈底元素,完成队列出队即将辅助栈栈顶元素出栈即可
public class MyQueue {
/**
* <h>用栈实现队列</h>
* <li>push(x) -- 将一个元素放入队列的尾部。</li>
* <li>pop() -- 从队列首部移除元素。</li>
* <li>peek() -- 返回队列首部的元素。</li>
* <li>empty() -- 返回队列是否为空。</li>
*
*/
//基于两个栈实现一个队列
Stack<Integer> stack;
Stack<Integer> temp;
/** Initialize your data structure here. */
public MyQueue() {
stack = new Stack<>();
temp = new Stack<>();
}
/** Push element x to the back of queue. */
public void push(int x) {
stack.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if(temp.isEmpty()) {
int size = stack.size();
for (int i = 0; i < size; i++) {
int data = stack.pop();
temp.push(data);
}
}
return temp.pop();
}
/** Get the front element. */
public int peek() {
if(temp.isEmpty()) {
int size = stack.size();
for (int i = 0; i < size; i++) {
int data = stack.pop();
temp.push(data);
}
}
return temp.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return temp.isEmpty() && stack.isEmpty();
}
}
其他数据结构实现栈
上边使用栈实现了一个简单的队列,其实也可以用栈来实现一个简单的队列
基于双端队列实现栈,栈的操作就是双端队列的队尾的操作
public class MyStack {
/**
* <h>队列实现栈</h>
* <li>push(x) -- 元素 x 入栈</li>
* <li>pop() -- 移除栈顶元素</li>
* <li>top() -- 获取栈顶元素</li>
* <li>empty() -- 返回栈是否为空</li>
*/
//也可以基于双端队列实现,deque实际上是由双向链表组成的双端队列 Linkedist implements Deque<T>
Deque<Integer> deque;
/** Initialize your data structure here. */
public MyStack() {
deque = new LinkedList<>();
}
/** Push element x onto stack. */
public void push(int x) {
deque.addLast(x);
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return deque.removeLast();
}
/** Get the top element. */
public int top() {
return deque.getLast();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return deque.isEmpty();
}
}
基于双向链表来实现栈,就是尾节点的部分的操作
public class MyStack {
/**
* <h>队列实现栈</h>
* <li>push(x) -- 元素 x 入栈</li>
* <li>pop() -- 移除栈顶元素</li>
* <li>top() -- 获取栈顶元素</li>
* <li>empty() -- 返回栈是否为空</li>
*/
//基于双向链表linkedList实现栈,linkedList尾部就是栈顶
List<Integer> list;
/** Initialize your data structure here. */
public MyStack() {
list = new LinkedList<>();
}
/** Push element x onto stack. */
public void push(int x) {
list.add(x);
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
int length = list.size();
return list.remove(length);
}
/** Get the top element. */
public int top() {
int length = list.size();
return list.get(length);
}
/** Returns whether the stack is empty. */
public boolean empty() {
return list.isEmpty();
}
}
逆序栈
比如 一个栈 入栈是 1 2 3 4 5 栈顶是5
我们想这个栈的 顺序变成 5 4 3 2 1 栈顶是1
递归实现
/**
* 递归获得栈底元素
* @param stack
* @return
*/
public static int getTop(Stack<Integer> stack){
int top = stack.pop();
if (stack.isEmpty()){
return top;
}
//下一个元素
int next = getTop(stack);
stack.push(top);
return next;
}
/**
* 将递归获得的栈底元素重新入栈
* @param stack
*/
public static void reverseStack(Stack<Integer> stack){
if(stack.isEmpty()){
return;
}
int next = getTop(stack);
reverseStack(stack);
stack.push(next);
}
辅助栈实现
/**
* 使用非递归双栈实现栈的逆序
*/
public static Stack reverse(Stack<Integer> stack){
Stack<Integer> tmp = new Stack<>();
while(!stack.isEmpty()){
tmp.push(stack.pop());
}
return tmp;
}
单栈实现
/**
* 非递归单栈解决栈逆序问题
* @param stack
* @return
*/
public static Stack reverse2(Stack<Integer> stack){
List<Integer> list = new ArrayList<>();
while(!stack.isEmpty()){
list.add(stack.pop());
}
while(list.size()!=0){
stack.push(list.remove(list.size()-1));
}
return stack;
}