算法3:链表实现双端队列

  • 我们在算法2中已经使用Node实现了链表的功能。此时,我们进一步对链表进行延伸。题目:“使用单链表实现队列,实现先进先出的功能”。
  • 解题思路: 既然是链表,那么必然有一个头节点和尾结点。先进先出,那就是从头节点取数据,从尾节点添加数据
  • package code.code_02;
    
    /**
     * 使用单链表设计出队列
     */
    public class SingleNodeQueue<V> {
    
        private Node<V> head; //当前队列的头
        private Node<V> tail;   //当前队列的尾
        private int size;
    
        public SingleNodeQueue () {
            head = null;
            tail = null;
            size = 0;
        }
        //用于单链表的节点
        private class Node<V> {
            public V data;
            public Node<V> next;
    
            Node (V _data){
                this.data = _data;
            }
            public V getData() {
                return data;
            }
        }
    
        public boolean isEmpty () {
            return size == 0 ? true : false;
        }
    
        public int size () {
            return size;
        }
    
        //获取链表的头
        public V peek ()
        {
            Node<V> node = null;
            Node<V> next = null;
            if (head == null) {
                System.out.println("当前队列还没有设置元素");
                tail = null;
                return null;
            }
            else {
                //提前记录下头节点和头节点的持有的下一个节点的引用
                node = head;
                next = head.next;
                //将头节持有的下一个节点引用释放掉
                head.next = null;
                //因为头节点的下一个节点之前被记录过
                //此时,头节点head会释放掉原始的内存对象
                // 并且来到新的头节点位置(也就是之前的第二个节点)
                head = next;
                size--;
            }
            //因为node是局部变量,方法调用完成以后,node所对应的内存对象会被回收
            return node.getData();
        }
    
        //获取链表的头, 此方法是对peek进行的优化
        public V peek1 ()
        {
            V value = null;
            if (head == null) {
                System.out.println("当前队列还没有设置元素");
                tail = null;
                return null;
            }
            else {
                value = head.getData();
                /**
                 * 此处优化非常的美妙, 一句话抵得上peek()方法中的4句
                 *  node = head;
                 *  next = head.next;
                 *  head.next = null;
                 *  head = next;
                 */
                head = head.next;
                size--;
            }
            return value;
        }
    
        //设置链表元素
        public void offer (V data) {
            Node node = new Node(data);
            if (tail == null) {
                head = node;
                tail = node;
            }
            else {
                tail.next = node;
                tail = node;
            }
            size++;
        }
    
        public static void main(String[] args) {
            SingleNodeQueue<Integer> queue = new SingleNodeQueue<>();
            queue.offer(1);
            queue.offer(2);
            queue.offer(3);
            System.out.println(queue.size());
    
            do {
                System.out.println("获取单向队列的元素: " + (queue.peek()));
                System.out.println("测试当前队列长度" + queue.size());
            }while(queue.size() > 0);
    
    
            System.out.println("================测试泛型,优化后的peek================================");
    
            SingleNodeQueue<String> queue1 = new SingleNodeQueue<>();
            queue1.offer("test 1");
            queue1.offer("test 2");
            queue1.offer("test 3");
            System.out.println(queue1.size());
    
            do {
                System.out.println("获取单向队列的元素: " + (queue1.peek1()));
                System.out.println("测试当前队列长度" + queue1.size());
            }while(queue1.size() > 0);
    
    
        }
    }

    打印结果如下:

    3
    获取单向队列的元素: 1
    测试当前队列长度2
    获取单向队列的元素: 2
    测试当前队列长度1
    获取单向队列的元素: 3
    测试当前队列长度0
    ================测试泛型,优化后的peek================================
    3
    获取单向队列的元素: test 1
    测试当前队列长度2
    获取单向队列的元素: test 2
    测试当前队列长度1
    获取单向队列的元素: test 3
    测试当前队列长度0
    
    Process finished with exit code 0

     

  • 下面继续延伸。题目 “使用双向链表的知识,设计一个双向队列。要求是队列的头和队列的尾,都可以添加数据并且取数据
    package code.code_02;
    
    /**
     * 使用双链表设计一个双向队列,使对象的头节点和尾节点都可以
     * 添加/弹出节点元素
     */
    public class DoubleNodeQueue<V> {
    
        private Node<V> head; //当前队列的头
        private Node<V> tail;   //当前队列的尾
        private int size;
    
        public DoubleNodeQueue() {
            head = null;
            tail = null;
            size = 0;
        }
    
        //双链表
        private static class Node<V> {
            public V data;
            public Node<V> last;
            public Node<V> next;
    
            Node (V _data){
                this.data = _data;
            }
            public V getData() {
                return data;
            }
        }
    
        public boolean isEmpty () {
            return size == 0 ? true : false;
        }
    
        public int size () {
            return size;
        }
    
        //从队列头部取元素
        public V peekHead ()
        {
            V value = null;
            if (head == null) {
                System.out.println("当前队列还没有设置元素");
                tail = null;
                return value;
            }
            else {
                value = head.getData();
                //此时, 队列的第二个元素成为新的
                //头节点head
                head = head.next;
                size--;
            }
            return value;
        }
    
        //从队列尾部取元素
        public V peekTail ()
        {
            V value = null;
            if (tail == null) {
                System.out.println("当前队列还没有设置元素");
                return value;
            }
            else {
                value = tail.getData();
                //如果头和尾相等,说明队列只有一个元素
                //等价与tail.last == null;
                if (head == tail) {
                    head = null;
                    tail = null;
                } else {
                    tail = tail.last;
                    tail.next = null;
                }
                size--;
            }
            return value;
        }
    
        //从尾节点添加队列元素
        public void pushTail (V data) {
            Node<V> node = new Node<>(data);
            if (head == null) {
                head = node;
                tail = node;
            }
            else {
                tail.next = node;
                //双向链表组成的队列新增部分
                //当前节点需要持有之前的尾结点对象引用
                node.last = tail;
                //添加完成以后, 尾节点需要移动到新添加的节点node处,
                //此时,新添加的node成为尾节点tail
                tail = node;
            }
            size++;
        }
    
        //从头节点添加队列元素
        public void pushHead (V data) {
            Node<V> node = new Node<>(data);
            if (head == null) {
                head = node;
                tail = node;
            }
            else {
                /**
                 * 从头结点添加队列新元素,那么之前的头节点变成第二个元素
                 * 因此,之前的头结点head.last需要持有新节点对象引用node
                 * 新节点node.next需要持有之间的头结点
                 */
                head.last = node;
                node.next = head;
                //添加完成以后, 头节点需要移动到新添加的节点node处,
                //此时,新添加的node成为头节点head
                head = node;
            }
            size++;
        }
    
        public static void main(String[] args) {
            DoubleNodeQueue<Integer> queue = new DoubleNodeQueue<>();
            //demo1, 测试从尾部添加元素,头部获取元素。 先进先出
            queue.pushTail(1);
            queue.pushTail(2);
            queue.pushTail(3);
    
            System.out.println("=============测试双向队列尾部添加元素,头部取元素—----先进先出==========================");
            do {
                System.out.println("获取单向队列的元素: " + (queue.peekHead()));
            }while(queue.size > 0);
    
            //demo2, 测试从尾部添加元素,从尾部获取元素
            queue.pushTail(-1);
            queue.pushTail(-2);
            queue.pushTail(-3);
    
            System.out.println("=============测试双向队列尾部添加元素,尾部获取元素—----后进先出==========================");
            do {
                System.out.println("获取单向队列的元素: " + (queue.peekTail()));
            }while(queue.size > 0);
    
            //demo3, 测试从头部添加元素,头部取元素
            queue.pushHead(11);
            queue.pushHead(12);
            queue.pushHead(13);
            System.out.println("=============测试双向队列头部添加元素,头部获取元素—----后进先出==================");
            do {
                System.out.println("获取单向队列的元素: " + (queue.peekHead()));
            }while(queue.size > 0);
    
            //demo4, 测试从头部添加元素, 尾部取元素
            queue.pushHead(21);
            queue.pushHead(22);
            queue.pushHead(23);
            System.out.println("=============测试双向队列头部添加元素,尾部获取元素—----先进先出==========================");
            do {
                System.out.println("获取单向队列的元素: " + (queue.peekTail()));
            }while(queue.size > 0);
    
        }
    }

    打印信息如下:

  • =============测试双向队列尾部添加元素,头部取元素—----先进先出==========================
    获取单向队列的元素: 1
    获取单向队列的元素: 2
    获取单向队列的元素: 3
    =============测试双向队列尾部添加元素,尾部获取元素—----后进先出==========================
    获取单向队列的元素: -3
    获取单向队列的元素: -2
    获取单向队列的元素: -1
    =============测试双向队列头部添加元素,头部获取元素—----后进先出==================
    获取单向队列的元素: 13
    获取单向队列的元素: 12
    获取单向队列的元素: 11
    =============测试双向队列头部添加元素,尾部获取元素—----先进先出==========================
    获取单向队列的元素: 21
    获取单向队列的元素: 22
    获取单向队列的元素: 23
    
    Process finished with exit code 0

     

  • 有了单链表的实现,相信双链表实现也能理解。如果此算法有问题,说明链表的知识掌握的还不够牢靠,请先看算法2的相关知识 https://www.cnblogs.com/chen1-kerr/p/16905803.html
posted @ 2022-11-20 22:35  街头小瘪三  阅读(60)  评论(0编辑  收藏  举报