双端队列,java语言描述并带类库分析.
双端队列
双端队列其实就是两边都可以进出,不再局限于先进先出。
双端队列的接口
public interface DequeInterface<T> { // 名字与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<T> implements DequeInterface<T> {
    private LinkedList<T> list;
    public LinkedDeque() {
        // 初始化链表
        list = new LinkedList<T>();
    }
    @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<T>();
        // 初始化属性
        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<T> implements DequeInterface<T> {
    // private LinkedList<T> list;
    // 使用一个值保存头结点,一个保存尾结点。 没有必要使用循环链表,因为判断起来比较麻烦。
    private Node first;
    private Node last;
    int size;
    public LinkedDeque() {
        // 初始化链表
        // list = new LinkedList<T>();
        // 初始化属性
        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);
        }
    }
}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号