作者:~小明学编程
文章专栏:Java数据结构
格言:目之所及皆为回忆,心之所想皆为过往
目录
栈
概念
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈,出数据在栈顶。
实现
1. 利用顺序表实现,即使用尾插 + 尾删的方式实现。
2. 利用链表实现,则头尾皆可(一般用双向链表)。
这里介绍一下用顺序表实现的方式。
public class MyStack {
private int[] elem;
private int usedSize=0;
public MyStack() {
this.elem = new int[5];
}
public void push(int val) {
if (isFull()) {
//扩容
this.elem = Arrays.copyOf(this.elem,2*this.elem.length);
}
this.elem[usedSize] = val;
this.usedSize++;
}
public int pop() {
if (isEmpty()) {
throw new RuntimeException("栈为空!");
}
usedSize--;
return this.elem[usedSize];
}
public int peek() {
if (isEmpty()) {
throw new RuntimeException("栈为空!");
}
return this.elem[usedSize-1];
}
public boolean isEmpty() {
return this.usedSize==0;
}
private boolean isFull() {
return this.usedSize==this.elem.length;
}
}
这边就是我们用顺序表实现栈的过程,代码量不是很大实现起来也比较简单。
队列
概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头。
实现
基于单链表实现队列
/**
* 利用单链表实现我们的队列
* 首先创建我们的链表节点
* */
class Node {
public int val;
public Node next;
public Node(int val) {
this.val = val;
}
}
public class MyQueue {
public Node head;
public Node last;
public void offer(int val) {
Node node = new Node(val);
if (this.head==null) {
this.head = node;
this.last = node;
} else {
this.last.next = node;
this.last = this.last.next;
}
}
public int pull() {
if (isEmpty()) {
throw new RuntimeException("队列为空!");
} else {
int oldVal = this.head.val;
this.head = this.head.next;
return oldVal;
}
}
public boolean isEmpty() {
return this.head == null;
}
public int peek() {
if (isEmpty()) {
throw new RuntimeException("队列为空!");
} else {
return this.head.val;
}
}
}
循环队列
实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列,环形队列通常使用数组实现。
循环队列可以充分的利用我们的资源。
数组下标循环技巧
1. 下标最后再往后(offset 小于 array.length): index = (index + offset) % array.length。
2. 下标最前再往前(offset 小于 array.length): index = (index + array.length - offset) % array.length。
判断队列的空与满
队列为空:
队列的空很容易判断,只要当我们的front与rear相等的时候就是队列为空的时候。
队列为满:
1.我们可以用一个变量usedSize来记录我们队列现有的元素没入队一个元素就+1,出队就-1,当usedSize的大小和我们数组的大小相同的时候就是队列满的时候。
2.我们可以Boolean一个flag标记一下,我们rear入队的时候就给它置为true,front出队的时候就给它置为false,当我们的rear==front&&flag==true的时候就是队列满的时候。
3.我们可以用 (this.rear+1)%elem.length==this.front) 这条语句来判断一下队列是否是满的当返回true的时候就是满的,不过这样也导致了一个缺点那就是我们的队列但会空一个用不了。
代码
public class MyCircularQueue {
private int[] elem;
private int rear;
private int front;
public MyCircularQueue(int k) {
this.elem = new int[k+1];
}
public boolean enQueue(int value) {
if (isFull()) {
return false;
} else {
this.elem[this.rear] = value;
this.rear = (this.rear+1)%this.elem.length;
return true;
}
}
public boolean deQueue() {
if (isEmpty()) {
return false;
} else {
this.front = (this.front+1) % this.elem.length;
return true;
}
}
public int Front() {
if (isEmpty()) {
return -1;
} else {
return this.elem[this.front];
}
}
public int Rear() {
if (isEmpty()) {
return -1;
} else {
if (this.rear==0) {
int index = this.elem.length-1;
return this.elem[index];
} else {
return this.elem[this.rear-1];
}
}
}
public boolean isEmpty() {
return this.front==this.rear;
}
public boolean isFull() {
if ((this.rear+1)%elem.length==this.front) {
return true;
} else {
return false;
}
}
}
双端队列(Deque)
概念
双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。
Java中的栈和队列
在Java中我们的队列的底层是由双向链表来实现的。
Stack
方法 | 解释 |
E push(E item) | 压栈 |
E pop() | 出栈 |
E peek() | 查看栈顶元素 |
boolean empty() | 判断栈是否为空 |
Queue
错误处理 | 抛出异常 | 返回特殊值 |
入队列 | add(e) | offer(e) |
出队列 | remove() | poll() |
队首元素 | element() | peek() |
Deque