概览
1.队列(Queue)
1.1 概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 的特性。
入队列:进行插入操作的一端称为队尾(Tail/Rear)
出队列:进行删除操作的一端称为队头(Head/Front)
图示:
1.2 仿写Queue接口
可以参考Jjava api文档,可以发现:
-
Queue继承自Collection接口,由LinkedList实现
-
一组方法通过特殊返回值通知调用者发生错误:
- boolean:offer(E e):插入
- E peek( ):查看
- E poll( ):删除
-
一组方法通过异常通知调用者方式错误:
- boolean add(E e):插入
- E element( ):查看
- E remove( ):删除
仿写真正的Queue接口,元素类型,固定成Integer类型,不用泛型
示例代码:
public interface Queue {
/**
* 通过抛出异常,通知错误
*/
/**
*
* 永远返回true
*/
default boolean add(Integer e){
if(offer(e)==false){
throw new IllegalStateException();
}
return true;
}
/**
* 查看队首元素
*/
default Integer element(){
Integer e = peek();
if(e==null){
throw new NoSuchElementException();
}
return e;
}
/**
* 返回并删除元素
*/
default Integer remove(){
Integer e = poll();
if(e==null){
throw new NoSuchElementException();
}
return e;
}
/**
* 通过特殊的返回值,通知错误
*/
/**
* 插入
* true成功,false 失败
*/
boolean offer(Integer e);
/**
* 查看队首元素
*/
Integer peek();
/**
* 返回并删除元素
*/
Integer poll();
}
1.3 循环队列的实现
1.3.1 不开辟额外空间
通过引入size来区分队列是否为空or满,不用开辟额外空间,即k为数组length
图示:
插入元素:
删除元素:
方法说明:
- 对于enQueue:如果队列不满,rear和size 进行++,最如果rear等于数组length,则将rear置为0
- 对于deQueue:如果队列不为空,size进行 --, front 进行++,最后如果front==数组length,则将front置为0
- 对于Front:不为空,返回 array[front]
- 对于Rear:不为空,如果rear==0了,则返回 array[length-1],否则返回array[rear-1]
- 对于isEmpty:size为0 则空
- 对于IsFull:size==数组length则满
代码示例:
public class MyCircularQueue {
private int[] array;
private int size;
private int front;
private int rear;
public MyCircularQueue(int k) {
array = new int[k];
size=0;
front = 0;
rear = 0;
}
public boolean enQueue(int value) {
if(isFull()) {
return false;
}
array[rear++] = value;
size++;
if(rear==array.length) {
rear = 0;
}
return true;
}
public boolean deQueue() {
if(isEmpty()) {
return false;
}
size--;
front++;
if(front==array.length) {
front = 0;
}
return true;
}
public int Front() {
if(isEmpty()) {
return -1;
}
return array[front];
}
public int Rear() {
if(isEmpty()) {
return -1;
}
if(rear == 0) {
return array[array.length-1];
}
return array[rear-1];
}
public boolean isEmpty() {
return size==0;
}
public boolean isFull() {
return size==array.length;
}
}
1.3.2 需要开辟一个额外空间
直接使用front 和 rear 判空和判满,需要多开辟一个空间,即k+1为数组length
图示:
插入元素:
删除元素:
方法说明:
- 对于enQueue:如果rear==k+1,则将rear置为0
- 对于deQueue:如果front==k+1,则将front置为0
- 对于Front:不为空,返回array[front]
- 对于Rear:不为空,且rear为0,返回array[k],否则返回array[rear-1]
- 对于IsEmpty:当front等于rear为空
- 对于IsFull:当 rear+1 % k+1 == front 则为满
代码示例:
public class MyCircularQueue {
private int[] array;
private int rear,front;
private int k;
public MyCircularQueue_self(int k) {
this.k = k;
array = new int[k+1];
front = rear = 0;
}
/**
* k+1 就是 数组length
* @param value
* @return
*/
public boolean enQueue(int value) {
if(isFull()){
// System.out.println("sasd");
return false;
}
array[rear++] = value;
if(rear==k+1){
rear = 0;
}
return true;
}
public boolean deQueue() {
if(isEmpty()){
return false;
}
front++;
if(front==k+1){
front = 0;
}
return true;
}
public int Front() {
if(isEmpty()){
return -1;
}
return array[front];
}
public int Rear() {
if(isEmpty()){
return -1;
}
if(rear==0){
return array[k];
}
return array[rear-1];
}
public boolean isEmpty() {
return front==rear;
}
public boolean isFull() {
return (rear+1)%(k+1)==front;
}
2. 双端队列 (Deque)
2.1 概念
双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。
那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。
2.2 仿写Deque接口
通过Java api文档可以发现,Deque继承自Queue
方法说明:
-
一组方法通过特殊值报错:
- boolean offerFirst/offerLast(E e):头插,尾插
- E peekFirst( )/peekLast( ):查看队首/队尾元素
- E pollFirst( )/pollLast( ):头删,尾删
-
一组方法通过异常通知调用者发生错误:
- void addFirst/addLast(E e):头插/尾插
- E removeFirst/removeLast( ):头删/尾删,返回并删除元素
- E getFirst/getLast( ):查看队首/队尾元素
进行仿写:
public interface Deque extends Queue {
//通过特殊值报错
boolean offerFirst(Integer e);
Integer peekFirst();
Integer pollFirst();
boolean offerLast(Integer e);
Integer peekLast();
Integer pollLast();
//通过抛出异常报错
default void addFirst(Integer e){
if(offerFirst(e)==false){
throw new IllegalStateException();
}
}
default Integer getFirst(){
Integer e = peekFirst();
if(e==null){
throw new NoSuchElementException();
}
return e;
}
default Integer removeFirst(){
Integer e = pollFirst();
if(e==null){
throw new NoSuchElementException();
}
return e;
}
default void addLast(Integer e){
if(offerLast(e)==false){
throw new IllegalStateException();
}
}
default Integer getLast(){
Integer e = peekLast();
if(e==null){
throw new NoSuchElementException();
}
return e;
}
default Integer removeLast(){
Integer e = pollLast();
if(e==null){
throw new NoSuchElementException();
}
return e;
}
//这组方法继承于Queue
default boolean offer(Integer e){
return offerLast(e);
}
default Integer peek(){
return peekFirst();
}
default Integer poll(){
return pollFirst();
}
//下面这组方法为栈的形态准备
default void push(Integer e){
addFirst(e);
}
default Integer pop(){
return removeFirst();
}
}
3. 栈 (Stack)
3.1 概念
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。
- 栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
- 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
- 出栈:栈的删除操作叫做出栈。出数据在栈顶
图示:
3.2 栈的实现
可以用数组来实现,但是链表效率更高一些。通过Java api 文档发现,LinkedList 实现了Deque接口,Deque又继承了Queue接口
对LinkedList的仿写:
public class LinkedList implements Deque{
private static class Node{
private Integer v;
Node prev;
Node next;
Node(Integer x){
v = x;
}
}
private Node head;
private Node tail;
private int size;
/**
* 头插
* @param e
* @return
*/
@Override
public boolean offerFirst(Integer e) {
Node node = new Node(e);
if(size==0){
head = tail = node;
}else{
node.next = head;
head.prev = node;
head = node;
}
return true;
}
/**
* 查看头部元素
* @return
*/
@Override
public Integer peekFirst() {
if(size==0){
return null;
}
return head.v;
}
/**
* 头删除
* @return
*/
@Override
public Integer pollFirst() {
if(size==0){
return null;
}
Integer e = head.v;
head = head.next;
if(head!=null){
head.prev=null;
}else{
tail = null;
}
return e;
}
/**
* 尾插
* @param e
* @return
*/
@Override
public boolean offerLast(Integer e) {
Node node = new Node(e);
return false;
}
/**
* 查看尾部元素
* @return
*/
@Override
public Integer peekLast() {
if(size==0){
return null;
}
return tail.v;
}
/**
* 尾删
* @return
*/
@Override
public Integer pollLast() {
if(size==0){
return null;
}
Integer e = tail.v;
tail = tail.prev;
if(tail!=null){
tail.next=null;
}else{
head = null;
}
size--;
return e;
}
}
因为LinkedList实现了Deque,而Deque又继承了Queue,所以可以发现:
作为栈使用时核心方法为:
- E push(E item):压栈
- E pop():出栈
- E peek():查看栈顶元素
- boolean isEmpety():判断栈是否为空
4.小结
java中的栈和队列实际上都是由LinkedList表现出来的,所以当:
4.1作为Stack使用
方法 | 解释 |
---|---|
E push(E item) | 压栈 |
E pop() | 出栈 |
E peek() | 查看栈顶元素 |
boolean isEmpety() | 判断栈是否为空 |
4.2 作为Queue使用
错误处理 | 抛出异常 | 返回特殊值 |
---|---|---|
入队列 | add(e) | offer(e) |
出队列 | remove() | poll() |
队首元素 | element() | peek() |