算法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