1 基础知识
https://blog.csdn.net/AlinaQ05/article/details/126196718
https://blog.csdn.net/qq_43647936/article/details/131940968
https://blog.51cto.com/bitzmbdp/6273652
以上为本篇总结的来源
1.1 栈
栈是一种仅支持在表尾进行插入和删除操作的线性表,这一端被称为栈顶,另一端被称为栈底。元素入栈指的是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;元素出栈指的是从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。栈中的元素遵守后进先出(LIFO)的原则.
-
栈的种类
栈的底层实现有两种,基于数组的实现:顺序栈(ArrayList);基于链表的实现:链式栈(LinkedList)。
1.2栈的实现
1.2.1 数组实现
// 数组栈
public class ArrayStack {
int maxSize;
int[] list; //数组模拟栈,数据放在该数组
int top = 0; //表示栈顶,初始化为0
// 初始化栈
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
list = new int[maxSize];
}
// 入栈操作
public void push(int val) {
if (top >= maxSize) {
System.out.println("The stack is max!");
} else {
list[top] = val;
top++;
}
}
// 出栈操作
public void pop() {
if (top == 0) {
System.out.println("Error! The stack is empty!");
} else {
top--;
}
}
// 打印栈
public void display() {
if(top == 0){
System.out.println("The stack is empty!");
return;
}
for(int i = 0; i < top; i++) {
System.out.print(list[i]+" ");
}
System.out.println();
}
// 清空栈
public void clear() {
top = 0;
}
// 判空
public boolean isEmpty() {
if (top == 0) {
return true;
} else {
return false;
}
}
// 返回栈顶元素
public Object peek() {
if (top != 0) {
return list[top-1];
} else {
return null;
}
}
public static void main(String[] args) {
ArrayStack arrayStack = new ArrayStack(5);
System.out.println(arrayStack.isEmpty());
arrayStack.push(1);
arrayStack.push(2);
arrayStack.push(3);
arrayStack.push(4);
arrayStack.display();
arrayStack.pop();
arrayStack.display();
arrayStack.push(5);
arrayStack.display();
System.out.println(arrayStack.peek());
System.out.println(arrayStack.isEmpty());
arrayStack.clear();
arrayStack.display();
System.out.println(arrayStack.peek());
System.out.println(arrayStack.isEmpty());
}
}
1.2.3 链表实现
// 链栈
public class LinkStack {
// 链栈的结点构造
class StackNode{
int val; // 每个节点存放的值
StackNode next; // 指向下一个节点
StackNode() {
}
StackNode(int val) {
this.val = val;
}
StackNode(int val, StackNode next) {
this.val = val;
this.next = next;
}
}
int size = 0;
StackNode topNode;
// 入栈操作!即把元素插入栈顶,和头插法相同
public void push (int val) {
StackNode newStackNode = new StackNode();
newStackNode.val = val;
if (topNode == null) {
topNode = newStackNode;
size ++;
} else {
newStackNode.next = topNode;
topNode = newStackNode;
size ++;
}
System.out.println(val + " has been stacked!");
}
// 出栈操作!top指针直接下移即可
public void pop () {
if (topNode == null) {
System.out.println("Error! The stack is empty!");
} else {
int popValue = topNode.val; // 接收一下出栈的元素,方便后面打印出来
topNode = topNode.next; // 栈顶指针(即链表头结点)直接指向下一个元素即可删除
size--;
System.out.println(popValue + " is out of stack!");
}
}
//打印栈 输出栈中的元素
public void display () {
StackNode current = topNode;
while(current != null){
System.out.print(current.val);
if (current.next != null) {
System.out.print(" ");
}
current = current.next;
}
System.out.println();
}
//返回栈顶元素
public int peek() {
if(topNode == null) {
System.out.println("The stack is empty!");
}
return topNode.val;
}
//清空
public void clear () {
if(topNode == null) {
System.out.println("The stack is empty!");
} else {
topNode = null;
System.out.println("Stack has been emptied!");
}
}
//是否为空
public boolean isEmpty () {
if (topNode == null) {
return true;
} else {
return false;
}
}
public static void main(String[] args) {
LinkStack linkStack = new LinkStack();
linkStack.push(2);
linkStack.push(3);
linkStack.display();
linkStack.pop();
linkStack.display();
System.out.println("The topNode is "+linkStack.peek());
linkStack.clear();
System.out.println("The stack is null? "+linkStack.isEmpty());
linkStack.push(8);
linkStack.push(7);
linkStack.push(5);
linkStack.display();
System.out.println("The stack is null? "+linkStack.isEmpty());
}
}
1.2.3 java导包实现栈
https://blog.51cto.com/bitzmbdp/6273652 来源
- 普通顺序栈
首先我们先学习普通顺序栈,创建一个栈得知道栈的类是是什么->Stack,并且还要导包,我们试着创建一个这个类的对象,代码实现如下:
import java.util.Stack;//栈的包
public class Test {
public static void main(String[] args) {
Stack</*数据类型*/> stack = new Stack</*数据类型*/>();
}
}
如上,一个顺序栈就创建好了,但是,在Java 1.4
及之前版本中,Stack
底层采用了Vector类
作为其内部实现(Vector是一种以动态数组实现的序列容器)。而在Java 1.5
之后的版本中,Stack
被废弃了,官方推荐使用Deque
或ArrayDeque
代替。而Deque
接口的其中一个实现就是LinkedList类
,也就是说,如果使用LinkedList
作为一个栈的基本数据结构,那么它底层的实现是基于双向链表的数据结构,而LinkedList
就是下文需要讲述的对象。
- 链式栈
通俗点来说就是以双链表为底层来实现的栈,在双链表尾部入栈,尾部出栈,或者头部入栈,头部出栈。上文说到Srack
不常用了,所以一般现在都用链式栈,链式栈的类是Deque
,它不仅仅可以当作栈,还可以当作队列,因为Deque类
里面还实现了队列的方法,下文会细说。创建链式栈代码实现如下:
import java.util.LinkedList;
import java.util.Queue;
public class Test {
public static void main(String[] args) {
Deque<Integer> stack = new LinkedList<>();
}
}
- 栈的常用方法
方法 | 功能 |
---|---|
Stack() | 创建一个空栈 |
E push(E e) | 返回值类型是入栈元素的类型,将e入栈,并返回e |
E pop() | 返回值类型入栈元素的类型,将栈顶元素出栈并返回 |
E peek() | 返回值类型入栈元素的类型,获取栈顶元素 |
int size() | 返回值类型是int类型,获取栈中有效元素个数 |
boolean empty() | 返回值类型是布尔类型,检测栈是否为空,为空返回true,否则返回false |
下面是每个方法的使用代码实现:
import java.util.Stack;
public class Test {
public static void main(String[] args) {
//Stack():创建栈,使用实现代码如下:
Stack<Integer> stack = new Stack<>();
//如上就创建了一个栈,栈里储存的数据类型是Integer类型
//push(E e):将元素入栈
int a = stack.push(1);
int b = stack.push(2);
stack.push(3);
stack.push(4);
stack.push(5);
System.out.println(a);
System.out.println(b);
System.out.println("==================");
// pop():出栈 将栈最顶上一个元素返回,并且删除该元素
int pop = stack.pop();
System.out.println(pop);
System.out.println("==================");
// peek():查看栈顶元素 就仅仅只是查看栈顶元素,但是不删除
int pek1 = stack.peek();
int pek2 = stack.peek();
System.out.println(pek1);
System.out.println(pek2);
System.out.println("==================");
//size():获取栈中有效元素个数
System.out.println(stack.size());
System.out.println("==================");
// empty():判断栈是否为空 判断栈内是否有元素,为空返回true,不为空返回false
System.out.println(stack.empty());
// 全部出栈,让栈空掉
while (!stack.empty()) {
stack.pop();
}
System.out.println(stack.empty());
}
}
1.3 队列
-
队列的概念
队列是一种常见的数据结构,它以先进先出方式处理数据。这意味着只有最早进入队列的元素才能首先被弹出,后来越晚加入的元素会排在较深的位置,等待之前元素全部出队后才能被依次出队。现实生活中也有很多例子,比如排队打饭,排队候车等等。
-
队列的种类
队列的种类也分两种,顺序队列和链式队列
-
顺序队列:底层是数组,从一端进,另一端出
-
链式队列:链式队列底层是链表,也是从一端进,另一端出
-
1.3.1 数组实现
package queue;
// 队列
public class ArrayQueue {
int maxSize; // 队列最大容量
int[] list; //数组模拟队列,数据放在该数组
int front = 0; //队头指针front,指向队头元素
int rear = 0; //队尾指针rear,指向下一个入队元素的存储位置
int currentSize; // 记录当前队列有几个元素
// 初始化队列
public ArrayQueue(int maxSize) {
this.maxSize = maxSize;
list = new int[maxSize];
front = 0;
rear = 0;
currentSize = 0;
}
// 入队
//将要入队的值存入数组中,同时队尾指针rear后移。
//如果队列已经满了,则添加失败。
//可以在队列满后创建一个更大的数组来优化操作
public void enQueue (int val) {
if (currentSize >= maxSize) {
System.out.println("Error! The queue is full!");
} else {
list[rear % maxSize] = val;
rear ++;
currentSize ++;
System.out.println(val + " has been queued!");
}
}
// 出队 队头指针front后移
public void deQueue () {
if (currentSize == 0) {
System.out.println("Error! The queue is empty!");
} else {
int dequeueValue = list[front];
front = (front + 1) % maxSize;
currentSize --;
System.out.println(dequeueValue + " is dequeued!");
}
}
//打印列
public void display () {
if(rear == front){
System.out.println("The queue is empty!");
return;
}
for(int i = front; i < rear; i++) {
System.out.print(list[i % maxSize]+" ");
}
System.out.println();
}
public static void main(String[] args) {
ArrayQueue arrayQueue = new ArrayQueue(9);
arrayQueue.enQueue(1);
arrayQueue.enQueue(2);
arrayQueue.enQueue(3);
arrayQueue.enQueue(4);
arrayQueue.enQueue(5);
arrayQueue.enQueue(6);
arrayQueue.enQueue(7);
arrayQueue.enQueue(8);
arrayQueue.enQueue(9);
arrayQueue.display();
arrayQueue.deQueue();
arrayQueue.enQueue(1);
arrayQueue.display();
}
}
1.3.2 链表实现
package queue;
// 链式队列
public class LinkQueue {
// 链式队列的结点构造
static class QueueNode{
int val;
QueueNode next;
// 初始化栈
QueueNode() {
}
QueueNode(int val) {
this.val = val;
}
QueueNode(int val, QueueNode next) {
this.val = val;
this.next = next;
}
}
int size = 0; // 记录当前队列有几个元素
QueueNode front; // 队头指针front,指向队头元素
QueueNode rear; // 队尾指针rear,指向下一个入队元素的存储位置
QueueNode back; // 队尾指针back,指向队尾元素
public void enQueue(int val) {
QueueNode queueNode = new QueueNode();
queueNode.val = val;
if (front == null) {
front = queueNode;
back = queueNode;
System.out.println(val + " has been queued!");
size ++;
} else {
back.next = queueNode;
back = back.next;
System.out.println(val + " has been queued!");
size ++;
}
}
public void deQueue() {
if (front == null) {
System.out.println("Error! The queue is empty!");
} else {
front = front.next;
}
}
public void display () {
QueueNode current = front;
while(current != null){
System.out.print(current.val);
if (current.next != null) {
System.out.print(" ");
}
current = current.next;
}
System.out.println();
}
public static void main(String[] args) {
LinkQueue linkQueue = new LinkQueue();
linkQueue.enQueue(1);
linkQueue.enQueue(2);
linkQueue.enQueue(3);
linkQueue.enQueue(4);
linkQueue.display();
linkQueue.deQueue();
linkQueue.display();
}
}
1.3.3 java导包实现
- 顺序队列
首先我们得知道队列的类是ArrayDeque
,它实现了Queue
接口,所以代码实现入下:
import java.util.ArrayDeque;
import java.util.Queue;
public class ArrayDequeQueueExample {
public static void main(String[] args) {
// 创建一个ArrayDeque作为队列
Queue</*元素类型*/> queue = new ArrayDeque<>();
}
}
- 链式队列
链式队列的类是LinkedList
,它也实现了Queue
接口,代码实现如下:
import java.util.LinkedList;
import java.util.Queue;
public class Test {
public static void main(String[] args) {
// 创建一个LinkedList作为队列
Queue<Integer> queue1 = new LinkedList<>();
}
}
- 队列的常用方法
方法 | 功能 |
---|---|
boolean offer(E e) | 返回值类型是布尔类型,入队列 |
E poll() | 返回值类型是队列中元素的类型,出队列 |
E peek() | 返回值类型是队列中元素的类型,获取队头元素 |
int size() | 返回值类型是int类型,获取队列中有效元素个数 |
boolean isEmpty() | 返回值类型是布尔类型,检测队列是否为空 |
import java.util.LinkedList;
import java.util.Queue;
public class Test {
public static void main(String[] args) {
// 创建一个LinkedList作为队列
Queue<Integer> queue = new LinkedList<>();
// boolean offer(E e):表示将元素入队列 入队成功返回true,失败则返回false
boolean falg = queue.offer(1);
queue.offer(2);
queue.offer(3);
queue.offer(4);
queue.offer(5);
System.out.println(falg);
System.out.println("==================");
// E poll():将元素弹出队列,返回该元素
int a = queue.poll();
int b = queue.poll();
System.out.println(a);
System.out.println(b);
System.out.println("==================");
// E peek():返回队头元素 返回队列头部元素,但是不删除
int c = queue.peek();
int d = queue.peek();
System.out.println(c);
System.out.println(d);
System.out.println("==================");
// int size():获取队列中有几个元素,返回元素个数
int size = queue.size();
System.out.println(size);
System.out.println("==================");
// boolean isEmpty():判空判断队列是否为空,为空返回true,否则返回false
falg = queue.isEmpty();
System.out.println(falg);
// 将队列全部弹出
while (!queue.isEmpty()) {
queue.poll();
}
falg = queue.isEmpty();
System.out.println(falg);
}
}
2 Leetcode题
232.用栈实现队列
使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。
示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
说明:
- 你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
- 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
- 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
思路:
用输入栈和输出栈实现队列
把出栈的元素放到输出栈中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wIyrFOE4-1691121265539)(C:\Users\86188\AppData\Roaming\Typora\typora-user-images\1691118196562.png)]
执行语句:
queue.push(1);
queue.push(2);
queue.pop(); 注意此时的输出栈的操作
queue.push(3);
queue.push(4);
queue.pop();
queue.pop();注意此时的输出栈的操作
queue.pop();
queue.empty();
-
**模拟入队:**在push数据的时候,只要数据放进输入栈就好,
-
模拟出队:输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。
-
如何判断队列为空呢?如果进栈和出栈都为空的话,说明模拟的队列为空了。
class MyQueue {
Stack<Integer> inStack;
Stack<Integer> outStack;
public MyQueue() {
inStack=new Stack<>();
outStack=new Stack<>();
}
public void push(int x) {
inStack.push(x);
}
public int pop() {
transfer();
return outStack.pop();
}
public int peek() {
transfer();
return outStack.peek();
}
public boolean empty() {
return inStack.isEmpty() && outStack.isEmpty();
}
public void transfer(){
if(!outStack.isEmpty()) return;
while(!inStack.isEmpty()){
outStack.push(inStack.pop());
}
}
225. 用队列实现栈
使用队列实现栈的下列操作:
- push(x) – 元素 x 入栈
- pop() – 移除栈顶元素
- top() – 获取栈顶元素
- empty() – 返回栈是否为空
注意:
-
你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
-
你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
-
你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。
-
使用两个队列实现
class MyStack {
//q1作为主要的队列,其元素排列顺序和出栈顺序相同
Queue<Integer> q1 = new ArrayDeque<>();
//q2仅作为临时放置
Queue<Integer> q2 = new ArrayDeque<>();
public MyStack() {
}
//在加入元素时先将q1中的元素依次出栈压入q2,然后将新加入的元素压入q1,再将q2中的元素依次出栈压入q1
public void push(int x) {
while (q1.size() > 0) {
q2.add(q1.poll());
}
q1.add(x);
while (q2.size() > 0) {
q1.add(q2.poll());
}
}
public int pop() {
return q1.poll();
}
public int top() {
return q1.peek();
}
public boolean empty() {
return q1.isEmpty();
}
}
- 使用一个队列实现
class MyStack {
Queue<Integer> queue;
public MyStack() {
queue = new LinkedList<>();
}
//每 offer 一个数(A)进来,都重新排列,把这个数(A)放到队列的队首
public void push(int x) {
queue.offer(x);
int size = queue.size();
//移动除了 A 的其它数
while (size-- > 1)
queue.offer(queue.poll());
}
public int pop() {
return queue.poll();
}
public int top() {
return queue.peek();
}
public boolean empty() {
return queue.isEmpty();
}
}