栈
概念
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的
一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
入栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶。
用图表示:
实现
- 顺序表:是有尾插操作表示 “入栈”, 使用尾删操作表示 “出栈”,使用根据下标获取元素的操作表示“取栈顶元素”
- 链表:使用头插操作表示 “入栈”,使用头删操作表示 “出栈”,直接去头结点就是 “取栈顶元素”。
栈的核心操作:
- 入栈:把元素放到栈里面
- 出栈:把最后进来的元素删掉
- 取栈顶元素:获取最后一个进来的结果
用顺序表实现栈
public class MyStack {
private int[] data = null;
private int size = 0;
private int capacity = 100;
public MyStack() {
data = new int[capacity];
}
public static void main(String[] args) {
MyStack s = new MyStack();
s.push(1);
System.out.println(s.peek());
}
// 扩容操作
public void realloc() {
capacity = 2 * capacity;
int[] newData = new int[capacity];
for (int i = 0; i < data.length; i++) {
newData[i] = data[i];
}
data = newData;
}
// 1. 入栈
public void push(int val) {
if (size > capacity) {
realloc();
}
data[size] = val;
size++;
}
// 2. 出栈
public Integer pop() {
if (size == 0) {
return null;
}
int ret = data[size - 1];
size--;
return ret;
}
// 3. 去栈顶元素
public Integer peek() {
if (size == 0) {
return null;
}
return data[size - 1];
}
}
用链表实现栈(不带傀儡节点)
class Node {
int val;
Node next;
public Node(int val) {
this.val = val;
}
}
// 链表实现的栈(不带傀儡节点)
public class MyStack1 {
private Node head = null;
public static void main(String[] args) {
MyStack1 s = new MyStack1();
s.push(2);
s.pop();
System.out.println(s.peek());
}
// 1. 入栈
public void push(int val) {
Node newNode = new Node(val);
if (head == null) {
head = newNode;
return;
}
newNode.next = head;
head = newNode;
}
// 2. 出栈
public Integer pop() {
if (head == null) {
return null;
}
if (head.next == null) {
int ret = head.val;
head = null;
return ret;
}
int ret = head.val;
head = head.next;
return ret;
}
// 3. 取栈顶元素
public Integer peek() {
if (head == null) {
return null;
}
return head.val;
}
}
用链表实现栈(带傀儡节点)
// 链表实现的栈(带傀儡节点)
public class MyStack2 {
Node head = null;
Node tail = null;
public MyStack2() {
head = new Node(0);
tail = head;
}
// 1. 入栈
public void push(int val) {
Node newNode = new Node(val);
tail.next = newNode;
tail = tail.next;
}
// 2. 出栈
public Integer pop() {
if (head == tail) {
return null;
}
int ret = head.next.val;
head = head.next;
return ret;
}
// 3. 取栈顶元素
public Integer peek() {
if (head == tail) {
return null;
}
return head.next.val;
}
队列
概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾(Tail/Rear)
出队列:进行删除操作的一端称为队头(Head/Front)
由于 Queue 是一个接口,不能实例化,在创建队列的时候不能直接 new Queue ,因此,需要 new 一个实现 Queue 接口的类。
如:
Queue<Integer> queue = new LinkedList<>();
图示:
实现
用顺序表实现队列
// 使用顺序边实现队列
public class MyQueue {
private int[] data = null;
private int head = 0;
private int tail = 0;
private int size = 0;
private int capacity = 100;
public MyQueue() {
data = new int[capacity];
}
// 扩容操作
public void realloc() {
capacity = 2 * capacity;
int[] newData = new int[capacity];
for (int i = 0; i < data.length; i++) {
newData[i] = data[i];
}
data = newData;
}
// 1. 入队列
public boolean offer(int val) {
if (size == data.length) {
// 可以进行扩容扩容操作或者直接 return false
// realloc();
return false;
}
data[size] = val;
tail++;
if (tail == data.length) {
tail = 0;
}
size++;
return true;
}
// 2. 出队列
public Integer poll() {
if (size == 0) {
return null;
}
int ret = data[head];
head++;
if (head == data.length) {
head = 0;
}
size--;
return ret;
}
// 3. 取队首元素
public Integer peek() {
if (size == 0) {
return null;
}
return data[head];
}
}
用链表实现队列
// 使用链表实现队列
public class MyQueue1 {
static class Node {
int val;
Node next;
public Node(int val) {
this.val = val;
}
}
private Node head = null;
private Node tail = null;
// 入队列
public boolean offer(int val) {
Node newNode = new Node(val);
if (head == null) {
head = newNode;
tail = newNode;
return true;
}
tail.next = newNode;
tail = tail.next;
return true;
}
// 出队列
public Integer poll() {
if (head == null) {
return null;
}
int ret = head.val;
if (head.next == null) {
head = null;
tail = null;
return ret;
}
head = head.next;
return ret;
}
// 取队首元素
public Integer peek() {
if (head == null) {
return null;
}
return head.val;
}
}
环形队列
单纯的使用顺序表势必会涉及到头插头删,效率低
因此,使用另一种方案,创建两个下标,分别表示队列的头部和队列的尾部
队列的有效元素区间 [head, tail)
入队列:把新的元素方法哦 tail 对应的下标上,同时 tail++
时间复杂度为 O(1)
出队列:把 head++,也就意味着把原来 head 只想的元素就排除到有效区间之外了
时间复杂度为 O(1)
环形队列为空的时候,此时 head 和 tail 是重合的
环形队列为满的时候,此时 head 和 tail 也是重合的
如何确定环形队列为空还是满?
- 不要把这个环形队列压榨干净,故意浪费一个空间
用 head 和 tail 重合表示队列为空,用 tail == head - 1 表示满队列 - 不浪费空间,使用一个 size 变量记录队列的元素个数,size== 0 就是空,size== 数组的长度 就是满