文章目录
栈(Stack)
栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶。
常用方法
方法 | 功能 |
---|---|
E push(E e) | 将e入栈,并返回e |
E pop() | 将栈顶元素出栈并返回 |
E peek() | 获取栈顶元素 |
int size() | 获取栈中有效元素个数 |
boolean empty() | 检测栈是否为空 |
栈,虚拟机栈,栈帧的区别
栈 | 虚拟机栈 | 栈帧 |
---|---|---|
数据结构 | JVM的一部分内存 | 调用方法时开辟的内存 |
逆波兰表达式求值
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for(int i = 0;i < tokens.length;i++) {
String str = tokens[i];
if(!isOperations(str)) {
//不是运算符 说明是数字,压入栈
stack.push(Integer.parseInt(str));
}else {
//是运算符
int num2 = stack.pop();
int num1 = stack.pop();
switch(str) {
case "+":
stack.push(num1+num2);
break;
case "-":
stack.push(num1-num2);
break;
case "*":
stack.push(num1*num2);
break;
case "/":
stack.push(num1/num2);
break;
}
}
}
return stack.pop();
}
/*判断当前字符串是不是一个运算符 */
private boolean isOperations(String str) {
return (str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/"));
}
}
有效的括号
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
//1. 遍历字符串
for(int i = 0;i < s.length();i++) {
char ch = s.charAt(i);
//2.是不是左括号
if(ch == '(' || ch == '[' || ch == '{') {
stack.push(ch);
}else {
//3.右括号
//3.1 栈为空
if(stack.isEmpty()) {
return false;
}
//3.2 栈不为空
char ch2 = stack.peek();//左括号
if(ch == ')' && ch2 == '(' || ch == ']'
&& ch2 == '[' || ch == '}' && ch2 == '{') {
stack.pop();
}else {
return false;
}
}
}
//字符串遍历完了 栈还是不为空
return stack.isEmpty();
}
}
最小栈
class MinStack {
Stack<Integer> stack;
Stack<Integer> minStack;
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
public void push(int val) {
stack.push(val);
if(minStack.empty()) {
minStack.push(val);
} else {
int peekNum = minStack.peek();
if(val<=peekNum) {
minStack.push(val);
}
}
}
public void pop() {
int val = stack.pop();
if(val == minStack.peek()) {
minStack.pop();
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
删除字符串中的所有相邻重复项
用字符串模拟栈
class Solution {
public String removeDuplicates(String s) {
StringBuilder ret = new StringBuilder();
char[] str = s.toCharArray();
for(char ch: str) {
if(ret.length() > 0 && ch == ret.charAt(ret.length() - 1)) {
//出栈
ret.deleteCharAt(ret.length() - 1);
} else {
//进栈
ret.append(ch);
}
}
return ret.toString();
}
}
比较含退格的字符串
class Solution {
public boolean backspaceCompare(String s, String t) {
return changeStr(s).equals(changeStr(t));
}
String changeStr(String str) {
StringBuilder ret = new StringBuilder();
for(int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if(ch != '#') {
//进栈
ret.append(ch);
} else {
//出栈
if(ret.length() > 0) ret.deleteCharAt(ret.length() - 1);
}
}
return ret.toString();
}
}
基本计算器II
class Solution {
public int calculate(String s) {
Stack<Integer> stack = new Stack<>();
char op = '+';
int i = 0, n = s.length();
char[] str = s.toCharArray();
while(i < n) {
if(str[i] == ' ') i++;
else if(str[i] >= '0' && str[i] <= '9') {
int tmp = 0;
while(i < n && str[i] >= '0' && str[i] <= '9') {
tmp = tmp * 10 + (str[i] - '0');
i++;
}
if(op == '+') stack.push(tmp);
else if(op == '-') stack.push(-tmp);
else if(op == '*') stack.push(stack.pop() * tmp);
else stack.push(stack.pop() / tmp);
} else {
op = str[i];
i++;
}
}
//统计结果
int ret = 0;
while(!stack.isEmpty()) {
ret += stack.pop();
}
return ret;
}
}
字符串解码
class Solution {
public String decodeString(String s) {
//先放一个空串
Stack<StringBuilder> stack = new Stack<>();
stack.push(new StringBuilder());
Stack<Integer> nums = new Stack<>();
int i = 0, n = s.length();
char[] str = s.toCharArray();
while(i < n) {
if(str[i] >= '0' && str[i] <= '9') {
int tmp = 0;
while(i < n && str[i] >= '0' && str[i] <= '9') {
tmp = tmp * 10 + (str[i] - '0');
i++;
}
nums.push(tmp);
} else if(str[i] == '[') {
i++;//把后面的字符串提取出来
StringBuilder tmp = new StringBuilder();
while(i < n && str[i] >= 'a' && str[i] <= 'z') {
tmp.append(str[i]);
i++;
}
stack.push(tmp);
} else if(str[i] == ']') {
//解析
StringBuilder tmp = stack.pop();
int k = nums.pop();
while(k-- != 0) {
stack.peek().append(tmp);
}
i++;
} else {
StringBuilder tmp = new StringBuilder();
while(i < n && str[i] >= 'a' && str[i] <= 'z') {
tmp.append(str[i]);
i++;
}
stack.peek().append(tmp);
}
}
return stack.peek().toString();
}
}
验证栈序列
class Solution {
public boolean validateStackSequences(int[] pushed, int[] popped) {
Stack<Integer> stack = new Stack<>();
int i = 0, n = popped.length;
for(int x: pushed) {
stack.push(x);
while(!stack.isEmpty() && stack.peek() == popped[i]) {
stack.pop();
i++;
}
}
//return i == n;
return stack.empty();
}
}
队列(Queue)
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 的原则
入队列:进行插入操作的一端称为队尾(Tail/Rear)
出队列:进行删除操作的一端称为队头(Head/Front)
常用方法
方法 | 功能 |
---|---|
boolean offer(E e) | 入队列 |
E poll() | 出队列 |
peek() | 获取队头元素 |
int size() | 获取队列中有效元素个数 |
boolean isEmpty() | 检测队列是否为空 |
注意:Queue是个接口,要实例化的话必须实例化LinkedList,因为LinkedList实现了Queue接口。
用栈实现队列
class MyQueue {
private Stack<Integer> s1 ;
private Stack<Integer> s2 ;
public MyQueue() {
s1 = new Stack<>();
s2 = new Stack<>();
}
public void push(int x) {
s1.push(x);
}
public int pop() {
if(empty()) {
return -1;
}
if(s2.isEmpty()) {
//弹出s1当中所有的元素 放到s2中
while(!s1.isEmpty()) {
s2.push(s1.pop());
}
}
return s2.pop();
}
public int peek() {
if(empty()) {
return -1;
}
if(s2.isEmpty()) {
//弹出s1当中所有的元素 放到s2中
while(!s1.isEmpty()) {
s2.push(s1.pop());
}
}
return s2.peek();
}
public boolean empty() {
return s1.isEmpty() && s2.isEmpty();
}
}
用队列实现栈
两个队列
class MyStack {
Queue<Integer> queue1;
Queue<Integer> queue2;
public MyStack() {
queue1 = new LinkedList<Integer>();
queue2 = new LinkedList<Integer>();
}
/** Push element x onto stack. */
public void push(int x) {
queue2.offer(x);
while (!queue1.isEmpty()) {
queue2.offer(queue1.poll());
}
Queue<Integer> temp = queue1;
queue1 = queue2;
queue2 = temp;
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return queue1.poll();
}
/** Get the top element. */
public int top() {
return queue1.peek();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return queue1.isEmpty();
}
}
一个队列
class MyStack {
Queue<Integer> queue;
/** Initialize your data structure here. */
public MyStack() {
queue = new LinkedList<Integer>();
}
/** Push element x onto stack. */
public void push(int x) {
int n = queue.size();
queue.offer(x);
for (int i = 0; i < n; i++) {
queue.offer(queue.poll());
}
}
/** 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.isEmpty();
}
}
设计循环队列
数组(创建时多一个元素)
数组下标循环的小技巧
-
下标最后再往后: index = (index + offset) % array.length
-
下标最前再往前: index = (index + array.length - offset) % array.length
当front与rear相等的时候,不知道是为空还是为满
所以front前面的元素不用来存放,front==rear时判空,(rear+1) % elem.length == front为满
class MyCircularQueue {
public int[] elem;
public int front;
public int rear;
public MyCircularQueue(int k) {
elem = new int[k+1];
}
//入队操作
public boolean enQueue(int value) {
if(isFull()) {
return false;
}
elem[rear] = value;
rear = (rear+1) % elem.length;
return true;
}
//删除队头元素
public boolean deQueue() {
if(isEmpty()) {
return false;
}
front = (front + 1) % elem.length;
return true;
}
//得到队头元素 不删除
public int Front() {
if(isEmpty()) {
return -1;
}
return elem[front];
}
//得到队尾元素 不删除
public int Rear() {
if(isEmpty()) {
return -1;
}
int index = (rear == 0) ? elem.length-1 : rear-1;
return elem[index];
}
//判空 front和rear相遇
public boolean isEmpty() {
return front == rear;
}
public boolean isFull() {
return (rear+1) % elem.length == front;
}
}
链表
class MyCircularQueue {
private ListNode head;
private ListNode tail;
private int capacity;
private int size;
public MyCircularQueue(int k) {
capacity = k;
size = 0;
}
public boolean enQueue(int value) {
if (isFull()) {
return false;
}
ListNode node = new ListNode(value);
if (head == null) {
head = tail = node;
} else {
tail.next = node;
tail = node;
}
size++;
return true;
}
public boolean deQueue() {
if (isEmpty()) {
return false;
}
head = head.next;
size--;
return true;
}
public int Front() {
if (isEmpty()) {
return -1;
}
return head.val;
}
public int Rear() {
if (isEmpty()) {
return -1;
}
return tail.val;
}
public boolean isEmpty() {
return size == 0;
}
public boolean isFull() {
return size == capacity;
}
}
双端队列 (Deque)
双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。
那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。
Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现