双端队列,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);
        }
    }

}

 

 

 

 

 

 

posted @ 2018-08-14 11:18  过道  阅读(136)  评论(0编辑  收藏  举报