栈
概念:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶。
数据结构的栈和jvm运行时内存区域中的stack是啥关系:
方法的调用涉及到栈帧,调用一个方法就压栈,方法执行完就出栈。栈帧中有局部变量表,局部变量表存放的是方法调用过程中的局部变量。
操作:
import java.util.Deque;
import java.util.LinkedList;
import java.util.Stack;
//如何实现栈
public class StackDemo {
public static void main(String[] args) {
//使用stack
// Stack<String> stack = new Stack<>();
//
// stack.push("A");
// stack.push("B");
// stack.push("C");
//
// System.out.println(stack.size()); // 3
// System.out.println(stack.isEmpty()); // false
// System.out.println(stack.empty()); // false
// System.out.println(stack);
//
// System.out.println(stack.peek()); // C
// System.out.println(stack.peek()); // C
// System.out.println(stack.pop()); // C
// System.out.println(stack.pop()); // B
//使用Deque
Deque<String> stack = new LinkedList<>();
stack.push("A");
stack.push("B");
stack.push("C");
System.out.println(stack);
System.out.println(stack.isEmpty()); // false
System.out.println(stack.size()); // 3
System.out.println(stack.peek()); // C
System.out.println(stack.peek()); // C
System.out.println(stack.pop()); // C
System.out.println(stack.pop()); // B
}
}
题目20:有效的括号
根据括号的匹配原则,选择数据结构为栈。
创建一个空栈,存储内容为字符,将字符串转化为字符数组;
遍历字符数组,如果是左括号就入栈,如果是右括号
- 如果栈为空,直接返回false
- 弹出栈顶元素看是否匹配,不匹配就直接返回false;匹配就继续遍历下一个字符
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
char [] chars = s.toCharArray();
for (char ch : chars){
if (ch == '(' || ch == '[' || ch == '{'){
stack.push(ch);
}else {
if (stack.empty()){
return false;
}
char left = stack.pop();
if (left == '(' && ch == ')'){
continue;
}else if (left == '[' && ch == ']'){
continue;
}else if (left == '{' && ch == '}'){
continue;
}else {
return false;
}
}
}
return stack.empty();
}
题目:剑指offer31栈的压入,弹出序列
private List<Integer> arrayToList(int []array){
List<Integer> list = new ArrayList<>(array.length);
for (int e: array){
list.add(e);
}
return list;
}
public boolean validateStackSequences(int[] pushed, int[] popped) {
List<Integer> pushedList = arrayToList(pushed);
List<Integer> popedList = arrayToList(popped);
Stack<Integer> stack = new Stack<>();
for (int e : pushedList){
if (!stack.isEmpty() && stack.peek() == e){
stack.pop();
continue;
}
while (true) {
if (pushedList.isEmpty()) {
return false;
}
int f = pushedList.remove(0);
if (f != e) {
stack.push(f);
} else {
break;
}
}
}
return stack.isEmpty();
}
题目225:用两个队列实现栈
方法一:
push:
1.选择一个队列(哪个队列中有数据选哪个,都没有就指定一个)
2.把元素加入队列中
pop:
1.选择一个队列(有数据的队列)
2.把该队列中的size - 1个数据移动到另一个队列中
3.把队列剩余的元素出掉
top:
1.选择一个队列(有数据的那个队列)
2.把该队列中的size-1个数据移动到另一个队列中
3.把队列剩余的返回,并且把该元素放入另一个队列中
方法二:
还有一种方法不必使用到两个队列,使用一个队列即可,先将所有元素入队列,如果要取元素就把前size-1个个元素又放回队列,将最后一个出队列,依次这样操作。
方法一的实现:
class MyStack {
private Queue<Integer> queue1 = new LinkedList<>();
private Queue<Integer> queue2 = new LinkedList<>();
/** Initialize your data structure here. */
public MyStack() {
}
/** Push element x onto stack. */
public void push(int x) {
Queue<Integer> queue;
if (!queue1.isEmpty()) {
queue = queue1;
} else if (!queue2.isEmpty()) {
queue = queue2;
} else {
queue = queue1;
}
queue.add(x);
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
Queue<Integer> queue;
Queue<Integer> toQueue;
if (!queue1.isEmpty()) {
queue = queue1;
toQueue = queue2;
} else {
queue = queue2;
toQueue = queue1;
}
int size = queue.size();
for (int i = 0; i < size - 1; i++) {
int e = queue.remove();
toQueue.add(e);
}
return queue.remove();
}
/** Get the top element. */
public int top() {
Queue<Integer> queue;
Queue<Integer> toQueue;
if (!queue1.isEmpty()) {
queue = queue1;
toQueue = queue2;
} else {
queue = queue2;
toQueue = queue1;
}
int size = queue.size();
for (int i = 0; i < size - 1; i++) {
int e = queue.remove();
toQueue.add(e);
}
int f = queue.remove();
toQueue.add(f);
return f;
}
/** Returns whether the stack is empty. */
public boolean empty() {
return queue1.isEmpty() && queue2.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
题目155:最小栈
本题要求的时间复杂度是O(1),push操作个peek操作原本就是O(1),获取栈中的最小元素,要么是遍历栈的元素得出最小元素,要么就是创建一个遍历记录,这里我们使用一个栈来进行记录,这样做的好处是利用了入栈和出栈O(1)的时间复杂度。
push :val入栈普通栈;最小栈:如果val<=最小栈的栈顶元素,那么该元素也入栈最小栈。
为什么这里是<=呢?很多人可能会疑问,如果等于此时最小栈的栈顶元素,那么最小值不是已经在最小栈中存在了吗,为什么还要入栈?因为我们忽略了一种特殊情况:
pop:普通栈出栈x,如果x==最小栈的栈顶元素那么最小栈才出栈。
class MinStack {
//正确
private Stack<Integer> normal = new Stack<>();
private Stack<Integer> min = new Stack<>();
/** initialize your data structure here. */
public MinStack() {
}
public void push(int val) {
normal.push(val);
if (min.isEmpty() || val <= min.peek()) {
min.push(val);
} else {
min.push(min.peek());
}
}
public void pop() {
normal.pop();
min.pop();
}
public int top() {
return normal.peek();
}
public int getMin() {
return min.peek();
}
}
队列
概念:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,具有先进先出的特点。
入队列:进行插入操作的一端称为队尾(Tail/Rear)
出队列:进行删除操作的一端称为队头(Head/Front)
尾插头删
操作:
import java.util.LinkedList;
import java.util.Queue;
public class QueneDemo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.add("A");
queue.add("B");
queue.add("B");
System.out.println(queue.isEmpty()); //判断队列是否为空
System.out.println(queue.size()); //队列的大小
System.out.println(queue.remove()); //出队
System.out.println(queue.element()); //查看队首元素
}
}
题目232:用栈实现队列
push:将数据放入栈2
pop:如果1号栈空了,把二号栈的所有元素放到一号栈,从栈1出数据
peek:如果栈1中有数据,直接从栈1中取数据,如果栈1没有数据,栈2中有数据就去将栈2中的所有元素放入栈1再从栈1取数据
class MyQueue {
private Stack<Integer> stack1 = new Stack<>();
private Stack<Integer> stack2 = new Stack<>();
/** Initialize your data structure here. */
public MyQueue() {
}
/** Push element x to the back of queue.
* 将元素 x 推到队列的末尾 */
public void push(int x) {
stack2.push(x);
}
/** Removes the element from in front of queue and returns that element.
* 从队列的开头移除并返回元素*/
public int pop() {
if (stack1.isEmpty()) {
// 把 stack2 的全部数据移入 stack1
// int size = stack2.size();
// for (int i = 0; i < size; i++) {
// int e = stack2.pop();
// stack1.push(e);
// }
while (!stack2.isEmpty()) {
int e = stack2.pop();
stack1.push(e);
}
}
return stack1.pop();
}
/** Get the front element.
* 返回队列开头的元素*/
public int peek() {
if (stack1.isEmpty()) {
while (!stack2.isEmpty()) {
int e = stack2.pop();
stack1.push(e);
}
}
return stack1.peek();
}
/** Returns whether the queue is empty.
* 如果队列为空,返回 true ;否则,返回 false*/
public boolean empty() {
return stack1.isEmpty() && stack2.isEmpty();
}
}
题目622:设计循环队列
但是rear==front的时候无法判断队列是空还是满。
为了达到判断循环队列满的条件,牺牲一个空间。
取队首元素:array[frontIndex]
取队尾元素:array[(rearIndex - 1 + array.length) % array.length]
队满:(rearIndex+1)%array.length == frontIndex
队空:rearIndex = = frontIndex
class MyCircularQueue {
private int[] array;
private int frontIndex;
private int rearIndex;
private int size;
public MyCircularQueue(int k) {
array = new int[k];
frontIndex = 0;
rearIndex = 0;
size = 0;
}
//插入
public boolean enQueue(int value) {
if (isFull()) {
return false;
}
array[rearIndex] = value;
rearIndex = (rearIndex + 1) % array.length;
size++;
return true;
}
//删除
public boolean deQueue() {
if (isEmpty()) {
return false;
}
frontIndex = (frontIndex + 1) % array.length;
size--;
return true;
}
//取队首
public int Front() {
if (isEmpty()) {
return -1;
}
return array[frontIndex];
}
//
public int Rear() {
if (isEmpty()) {
return -1;
}
int index = (rearIndex - 1 + array.length) % array.length;
return array[index];
}
public boolean isEmpty() {
return size == 0;
}
public boolean isFull() {
return size == array.length;
}
}