【队列和栈基础】
一、Java中的栈(先进后出,左边栈底,右边栈顶)
1、Stack类创建栈和使用栈
import java.util.Stack;
public class StackExample {
public static void main(String[] args) {
// 创建一个Stack对象
Stack<String> stack = new Stack<>();
// 添加元素到堆栈中
stack.push("元素1");
stack.push("元素2");
stack.push("元素3");
// 获取堆栈的大小
int size = stack.size();
System.out.println("堆栈的大小:" + size);
// 检查堆栈是否为空
boolean isEmpty = stack.isEmpty();
System.out.println("堆栈是否为空:" + isEmpty);
// 查看堆栈顶部的元素,但不移除它
String topElement = stack.peek();
System.out.println("堆栈顶部的元素:" + topElement);
// 移除堆栈顶部的元素
String poppedElement = stack.pop();
System.out.println("移除的元素:" + poppedElement);
// 再次查看堆栈顶部的元素
topElement = stack.peek();
System.out.println("堆栈顶部的元素:" + topElement);
// 遍历堆栈中的元素
System.out.println("遍历堆栈中的元素:");
while (!stack.isEmpty()) {
String element = stack.pop();
System.out.println(element);
}
}
}
上面代码的输出:
堆栈的大小:3
堆栈是否为空:false
堆栈顶部的元素:元素3
移除的元素:元素3
堆栈顶部的元素:元素2
遍历堆栈中的元素:
元素2
元素1
2、用LinkedList实现栈
import java.util.LinkedList;
public class MyStack {
private LinkedList<String> stack;
public MyStack() {
stack = new LinkedList<>();
}
public void push(String element) {
stack.addLast(element);
}
public String pop() {
return stack.removeLast();
}
public String peek() {
return stack.getLast();
}
public int size() {
return stack.size();
}
public boolean isEmpty() {
return stack.isEmpty();
}
}
注意:Stack类是继承自Vector类的经典栈数据结构,而LinkedList是一个实现了List接口的双向链表。官方建议使用LinkedList来替代Stack类实现栈的功能,因为LinkedList的性能更好。
二、队列(先进先出,左边队头,右边队尾)
import java.util.LinkedList;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<>();
// 向队尾插入元素
queue.offer(1);
queue.offer(2);
queue.offer(3);
// 查看队首元素
System.out.println("队首元素:" + queue.peek());
// 出队
int removedElement = queue.poll();
System.out.println("出队元素:" + removedElement);
// 再次查看队首元素
System.out.println("队首元素:" + queue.peek());
}
}
上面代码的输出:
队首元素:1
出队元素:1
队首元素:2
注意:
1、Queue接口定义了一些用于添加、删除和检索元素的方法,如 offer() 、 poll() 、peek()。
2、LinkedList是Java集合框架中的一个类,它实现了List接口,并且也实现了Queue接口。由于LinkedList是一个双向链表,它具有在两端进行高效插入和删除操作的特性,非常适合实现队列的功能。因此,可以利用多态的写法定义Queue对象,如:Queue<Integer> queue = new LinkedList<>();。
3、通过LinkedList实现的Queue具有队列的特性,可以使用 offer() 方法向队列尾部添加元素,使用 poll() 方法从队列头部移除并返回元素,使用 peek() 方法获取队列头部的元素但不移除它。
【232.用栈实现队列】
class MyQueue {
private Stack<Integer> stackIn; // 负责进队
private Stack<Integer> stackOut; // 负责出队
public MyQueue() {
this.stackIn = new Stack<>();
this.stackOut = new Stack<>();
}
public void push(int x) {
stackIn.push(x);
}
public int pop() {
// 注意:只有stackOut空的时候倒过去才符合队列的顺序
if (stackOut.isEmpty()){
pour();
}
return stackOut.pop();
}
public int peek() {
if (stackOut.isEmpty()){
pour();
}
return stackOut.peek();
}
public boolean empty() {
return stackIn.isEmpty() && stackOut.isEmpty();
}
private void pour(){
while(!stackIn.isEmpty()){
stackOut.push(stackIn.pop());
}
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
时间复杂度: O(n),pop()和peak()都要用pour(),都是O(n), empty()和push()都是O(1)
空间复杂度: O(n),两个栈 stackIn 和 stackOut都为 O(n)
【225. 用队列实现栈】
方法一 用两个队列实现栈,push简单,pop难
push:
pop:
class MyStack {
Queue<Integer> queue1;
Queue<Integer> queue2;
public MyStack() {
queue1 = new LinkedList<>(); // 负责进栈、存储栈元素
queue2 = new LinkedList<>(); // 辅助出栈
}
public void push(int x) {
queue1.offer(x);
}
public int pop() {
int popElement = 0; // 必须初始化否则会报错,保证在栈非空情况下调用pop的前提下,初始值可以为任意值
// 1、将除了栈顶元素之外的元素压进queue2,获取栈顶元素
while(!queue1.isEmpty()){
popElement = queue1.poll();
// 如果queue1弹出元素后就空了,证明弹出的元素是队尾元素,不需要再进queue2队
if (!queue1.isEmpty()){
queue2.offer(popElement);
}
}
// 2、将queue1和queue2交换,让queue1始终负责进栈
Queue<Integer> queue = queue1;
queue1 = queue2;
queue2 = queue;
return popElement;
}
public int top() {
// 利用出栈获取队尾元素
int q = pop();
// 再进栈恢复原始栈
push(q);
return q;
}
public boolean empty() {
return queue1.isEmpty(); // queue1始终用于存储栈元素,判断queue1是否空即可
}
}
/**
* 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();
*/
时间复杂度: O(n),pop()和top()都是O(n), empty()和push()都是O(1)
空间复杂度: O(n),使用的三个队列都为 O(n)
方法二 用两个队列实现栈,push难,pop简单
push:
pop:
class MyStack {
Queue<Integer> queue1;
Queue<Integer> queue2;
public MyStack() {
queue1 = new LinkedList<>();
queue2 = new LinkedList<>();
}
public void push(int x) {
// 1、先将x放入queue2暂存
queue2.offer(x);
// 2、再将queue1的元素压进queue2
while(!queue1.isEmpty()){
queue2.offer(queue1.poll());
}
// 3、将queue2和queue1互换
Queue<Integer> queue = queue1;
queue1 = queue2;
queue2 = queue;
}
public int pop() {
return queue1.poll();
}
public int top() {
return queue1.peek();
}
public boolean empty() {
return queue1.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();
*/
时间复杂度: O(n),只有push()是O(n),其余都是O(1)
空间复杂度: O(n),使用的三个队列都为 O(n)
方法三 优化版,只用一个队列实现(对应方法一的优化)
class MyStack {
Queue<Integer> queue;
int size;
public MyStack() {
queue = new LinkedList<>();
size = 0;
}
public void push(int x) {
queue.offer(x);
size++;
}
public int pop() {
// 1、除了栈顶以外的元素循环出队进队
for (int i = 0; i < size - 1; i++){
queue.offer(queue.poll());
}
// 2、弹出栈顶元素
size--;
return queue.poll();
}
public int top() {
int q = pop();
push(q);
return q;
}
public boolean empty() {
return queue.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();
*/
时间复杂度: O(n),pop()和top()都是O(n), empty()和push()都是O(1)
空间复杂度: O(n),使用一个队列为 O(n)
方法四 优化版,只用一个队列实现(对应方法二的优化)
class MyStack {
Queue<Integer> queue;
int size;
public MyStack() {
queue = new LinkedList<>();
size = 0;
}
public void push(int x) {
// 1、先将x压进栈
queue.offer(x);
size++;
// 2、再调整顺序使x为栈顶元素
for (int i = 0; i < size - 1; i++){
queue.offer(queue.poll());
}
}
public int pop() {
size--;
return queue.poll();
}
public int top() {
return queue.peek();
}
public boolean empty() {
return queue.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();
*/
时间复杂度: O(n),只有push()是O(n),其余都是O(1)
空间复杂度: O(n),使用一个队列为 O(n)