双端队列
双端队列其实就是两边都可以进出,不再局限于先进先出。
双端队列的接口
public interface DequeInterface { // 名字与java自身类库稍有不同,功能类似
public void addToFront(T newEntry);
public void addToBack(T newEntry);
public T removeFront();
public T removeBack();
public T getFront();
public T getBack();
public boolean isEmpty();
public void clear();
public int size();
}
我们选择使用双向链表实现双端队列。为了简单起见,我先使用LinkedList来实现这个接口以便读者了解各个方法,然后逐渐转化成自己手写的Deque.,LinkedList既是双向链表.
实现接口的类
import java.util.LinkedList;
/**
* Description:
*
* @ClassName: 双向链表/LinkedList实现双端队列。
* @author 过道
* @date 2018年8月14日 上午9:10:57
*/
public class LinkedDeque implements DequeInterface {
private LinkedList list;
public LinkedDeque() {
// 初始化链表
list = new LinkedList();
}
@Override
public void addToFront(T newEntry) {
// 从队列的头部近入
list.addFirst(newEntry);
}
@Override
public void addToBack(T newEntry) {
// 从队列的尾部近入
list.addLast(newEntry);
}
@Override
public T removeFront() {
// 移除首位元素
return list.removeFirst();
}
@Override
public T removeBack() {
// 移除尾部元素
return list.removeLast();
}
@Override
public T getFront() {
// 类似于peek(),将首位元素返回,不进行操作。
return list.getFirst();
}
@Override
public T getBack() {
// 得到尾部元素,不进行操作
return list.getLast();
}
@Override
public boolean isEmpty() {
// 容量,可以选择size==0,也可以直接调用siEmpty
return list.isEmpty();
}
@Override
public void clear() {
// 清空即可
list.clear();
}
@Override
public int size() {
return list.size();
}
}
接下来的任务就很清楚了,自定义一个双向链表,替换掉LinkedList。
定义Node类
class Node {
T data;
Node prev;//指向前一节点
Node next;//指向后一节点
public Node(T data, Node prev, Node next) {
this.data = data;
this.prev = prev;
this.next = next;
}
public Node(T data) {
this(data, null, null);
}
}
其实,现在问题就是如何实现双向链表,用自己的双向链表替换掉LinkedList即可.
用到的各个字段,构造器
// 使用一个值保存头结点,一个保存尾结点。 没有必要使用循环链表,因为判断起来比较麻烦。
private Node first;
private Node last;
int size;
public LinkedDeque() {
// 初始化链表
// list = new LinkedList();
// 初始化属性
first = null;
last = null;
size = 0; // 虽然有默认初始化,此处仍需初始化,养成一个好的编码习惯
}
各种方法实现
@Override
public void addToFront(T newEntry) {
// 从队列的头部近入
Node newNode = new Node(newEntry);
if (isEmpty()) {
// 尾结点也是头结点 头结点也是尾结点
last = newNode;
} else {
// 新节点与首节点建立双向链接
first.prev = newNode;
newNode.next = first;
}
first = newNode;
size++;
// list.addFirst(newEntry);
}
@Override
public void addToBack(T newEntry) {
// 从队列的尾部近入
Node newNode = new Node(newEntry);
if (isEmpty()) {
// 尾结点也是头结点 头结点也是尾结点
first = newNode;
} else {
// 新节点与尾节点建立双向链接
newNode.prev = last;
last.next = newNode;
}
last = newNode;
size++;
// 从队列的尾部近入
// list.addLast(newEntry);
}
@Override
public T removeFront() {
// 移除首位元素
// return list.removeFirst();
if (isEmpty()) {
// 抛出异常
}
Node front = first;
// 首节点后移
first = first.next;
if (first == null) {
last = null;
} else {
// 断开双向链接
first.prev = null;
front.next = null;
}
size--;
return front.data;
}
@Override
public T removeBack() {
if (isEmpty()) {
// 抛出异常
}
Node back = last;
last = last.prev;
if (last == null) {
first = null;
} else {
// 断开双向链接
last.next = null;
back.prev = null;
}
size--;
return back.data;
// 移除尾部元素
// return list.removeLast();
}
@Override
public T getFront() {
// 类似于peek(),将首位元素返回,不进行操作。
// return list.getFirst();
if (isEmpty()) {
// 抛出异常
}
return first.data;
}
@Override
public T getBack() {
// 得到尾部元素,不进行操作
// return list.getLast();
if (isEmpty()) {
// 抛出异常
}
return last.data;
}
@Override
public boolean isEmpty() {
// 容量,可以选择size==0,也可以直接调用siEmpty
// return list.isEmpty();
// 当first 和 back 都为空时,双向链表为空
return (first == null) && (last == null);
}
@Override
public void clear() {
// 双向链表实现,必须考虑两个对象相互引用的情况,如果出现,那么GC就不会清理这个内存,从而导致内存泄漏。
// 所以我们需要遍历链表并将其置为null
for (Node x = first; x != null && x != last;) {
Node next = x.next;
x.data = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
// 清空即可
// list.clear();
}
@Override
public int size() {
return size;
// return list.size();
}
所有源码
package unit10.queue;
import java.util.LinkedList;
/**
* Description:
*
* @ClassName: 双向链表实现双端队列。
* @author 过道
* @date 2018年8月14日 上午9:10:57
*/
public class LinkedDeque implements DequeInterface {
// private LinkedList list;
// 使用一个值保存头结点,一个保存尾结点。 没有必要使用循环链表,因为判断起来比较麻烦。
private Node first;
private Node last;
int size;
public LinkedDeque() {
// 初始化链表
// list = new LinkedList();
// 初始化属性
first = null;
last = null;
size = 0; // 虽然有默认初始化,此处仍需初始化,养成一个好的编码习惯
}
@Override
public void addToFront(T newEntry) {
// 从队列的头部近入
Node newNode = new Node(newEntry);
if (isEmpty()) {
// 尾结点也是头结点 头结点也是尾结点
last = newNode;
} else {
// 新节点与首节点建立双向链接
first.prev = newNode;
newNode.next = first;
}
first = newNode;
size++;
// list.addFirst(newEntry);
}
@Override
public void addToBack(T newEntry) {
// 从队列的尾部近入
Node newNode = new Node(newEntry);
if (isEmpty()) {
// 尾结点也是头结点 头结点也是尾结点
first = newNode;
} else {
// 新节点与尾节点建立双向链接
newNode.prev = last;
last.next = newNode;
}
last = newNode;
size++;
// 从队列的尾部近入
// list.addLast(newEntry);
}
@Override
public T removeFront() {
// 移除首位元素
// return list.removeFirst();
if (isEmpty()) {
// 抛出异常
}
Node front = first;
// 首节点后移
first = first.next;
if (first == null) {
last = null;
} else {
// 断开双向链接
first.prev = null;
front.next = null;
}
size--;
return front.data;
}
@Override
public T removeBack() {
if (isEmpty()) {
// 抛出异常
}
Node back = last;
last = last.prev;
if (last == null) {
first = null;
} else {
// 断开双向链接
last.next = null;
back.prev = null;
}
size--;
return back.data;
// 移除尾部元素
// return list.removeLast();
}
@Override
public T getFront() {
// 类似于peek(),将首位元素返回,不进行操作。
// return list.getFirst();
if (isEmpty()) {
// 抛出异常
}
return first.data;
}
@Override
public T getBack() {
// 得到尾部元素,不进行操作
// return list.getLast();
if (isEmpty()) {
// 抛出异常
}
return last.data;
}
@Override
public boolean isEmpty() {
// 容量,可以选择size==0,也可以直接调用siEmpty
// return list.isEmpty();
// 当first 和 back 都为空时,双向链表为空
return (first == null) && (last == null);
}
@Override
public void clear() {
// 双向链表实现,必须考虑两个对象相互引用的情况,如果出现,那么GC就不会清理这个内存,从而导致内存泄漏。
// 所以我们需要遍历链表并将其置为null
for (Node x = first; x != null && x != last;) {
Node next = x.next;
x.data = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
// 清空即可
// list.clear();
}
@Override
public int size() {
return size;
// return list.size();
}
class Node {
T data;
Node prev;
Node next;
public Node(T data, Node prev, Node next) {
this.data = data;
this.prev = prev;
this.next = next;
}
public Node(T data) {
this(data, null, null);
}
}
}