首先创建两个类,一个链表类,一个测试类。
创建静态内部类Node:
链表类中写一个静态内部类Node,内部类中有三个属性,Node类型的prev(相当于指向前一个节点的属性),Node类型的next(相当于指向后一个节点的属性)和Integer类型的值(value),写上有参构造更方便的初始化。
static class Node { private Node prev; private Node next; private Integer value; public Node(Node prev, Integer value, Node next) { this.prev = prev; this.value = value; this.next = next; } }
初始化哨兵:
在链表类中写上Node类型的sentinel属性(哨兵),将sentinel初始化,因为sentinel还没有初始化完成所以sentinel的next和prev只能先指向null,我们将sentinel的value值设置成为-1以作区分,然后在链表类的构造器中初始化sentinel(哨兵)属性将它的prev和next设置成它自己(sentinel),这样就初始化完成了。
private Node sentinel = new Node(null, -1, null); public DoubleRingLinked() { sentinel.prev = sentinel; sentinel.next = sentinel; }
创建头插入值方法:
方法名addFirst,参数是Integer类型的value,返回值为空(void),创建两个Node属性a和b,将a赋值sentinel(哨兵),将b赋值哨兵的后一个Node节点sentinel.next,新建一个Node节点它的prev属性就是a,value就是参数的value,它的next就是b,然后将a节点的next属性指向新建的Node节点,将b节点的prev属性指向新建的Node节点。
(如图所示如果想要头插入节点就在a和b中间创建一个Node节点让创建的节点前指针(prev)指向a,后指针(next)指向b,然后a.next指向新创建的节点,b.prev指向新创建的节点)
public void addFirst(Integer value) { Node a = sentinel; Node b = sentinel.next; Node node = new Node(a, value, b); a.next = node; b.prev = node; }
创建尾插入值方法:
方法名addLast,参数Integer类型的value,返回值为空(void),创建两个Node类型的属性a和b,将a赋值成sentinel.prev(哨兵的前一个节点),将b设置成sentinel(哨兵)新创建一个Node节点调用有参构造来初始化Node节点prev属性指向a,next属性指向b,然后a.next指向新的Node节点,b.prev指向新的Node节点。
(因为是环形链表所以我们将两个Node调换一下位置还将新建的Node插入a节点和b节点,步骤和头插一样)
public void addLast(Integer value) { Node a = sentinel.prev; Node b = sentinel; Node node = new Node(a, value, b); a.next = node; b.prev = node; }
重写Iterable接口:
实现Iterable接口中的Iterator方法,返回新建的Iterator方法,重写hasNext和next方法,新创建一个Node类型的p属性它指向哨兵的下一个节点这样在循环遍历的时候就不遍历哨兵节点了,hasNext方法是判断是否有下一个节点的,因为是循环链表所以直接返回判断是否p为sentinel(哨兵)就行,next方法是返回节点的值(value)然后将p指针指向下一个节点的所以先调用p.value得到p指向节点的值(value),然后p = p.next将p指向下一个节点。
public class DoubleRingLinked implements Iterable<Integer>{ @Override public Iterator<Integer> iterator() { return new Iterator<Integer>() { private Node p = sentinel.next; @Override public boolean hasNext() { return p != sentinel; } @Override public Integer next() { Integer value = p.value; p = p.next; return value; } }; } }
测试类:
测试头插值的方法所以先实例化链表类(DoubleLinkedList)得到链表类对象,然后调用链表类中的addFirst方法DoubleLinkedList.addFirst然后再用增强for循环遍历链表。
public class DoubleRingLinkedTest { DoubleRingLinked doubleRingLinked = new DoubleRingLinked(); @Test public void test1() { doubleRingLinked.addFirst(1); doubleRingLinked.addFirst(2); doubleRingLinked.addFirst(3); doubleRingLinked.addFirst(4); doubleRingLinked.addFirst(5); for (Integer value: doubleRingLinked) { System.out.println(value); } } }
结果:
5 4 3 2 1
(因为是头插法所以结果是倒过来的)
尾插法和头插法一样就是调用方法改成addLast。
@Test public void test2() { doubleRingLinked.addLast(1); doubleRingLinked.addLast(2); doubleRingLinked.addLast(3); doubleRingLinked.addLast(4); doubleRingLinked.addLast(5); for (Integer value: doubleRingLinked) { System.out.println(value); } }
结果:
1 2 3 4 5
头删除法:
方法名removeFirst,参数为空,返回值为空(void)先调用哨兵的next属性得到要删除的Node节点,判断该Node节点是否是哨兵如果是就抛出异常,创建Node类型的变量a和b,将a赋值哨兵,将b赋值要删除节点的后一个节点,然后将a的后一个节点指向b节点,然后将b的前一个节点指向a。
public void removeFirst() { Node removeNode = sentinel.next; if (removeNode == sentinel) { throw new IllegalArgumentException("不合法"); } Node a = sentinel; Node b = removeNode.next; a.next = b; b.prev = a; }
尾删除法:
因为是环形链表和头删除法类似只需要将要删除的Node节点放到哨兵节点和要删除节点的前一个节点之间就可以。
public void removeLast() { Node removeNode = sentinel.prev; if (removeNode == sentinel) { throw new IllegalArgumentException("不合法"); } Node a = removeNode.prev; Node b = sentinel; a.next = b; b.prev = a; }
按值删除法:
我们有时需要按照值来删除某个Node节点所以我需要两个方法一个是删除Node节点的方法,一个是通过值来找到Node节点的方法,删除Node节点的方法名是removeByValue,参数类型是Integer的value参数,返回值是空(void),通过值来找到Node节点的方法的方法名是findByValue,参数类型是Integer的value参数,返回值是Node,我们先说findByValue方法。
findByValue方法:我需要一个指针p来指向哨兵的后一个节点,因为是环形链表所以最后会回到哨兵节点所以我们通过while来循环p指针指向后一个节点然后如果找到了就返回Node节点如果找不到就返回一个Null。
public Node findByValue(Integer value) { Node p = sentinel.next; while (p != sentinel) { if (p.value == value) { return p; } p = p.next; } return null; }
removeByValue方法:我们先通过findByValue方法来得到要删除的Node节点,这时需要判断一下removeNode是否为空如果为空就return直接退出,如果不为空就用removeNode节点得到它的前后节点,然后就使用它的前节点的next属性指向它的后一个节点,用它的后节点的prev属性指向它的前一个节点。
public void removeByValue(Integer value) { Node removeNode = findByValue(value); if (removeNode == null) { return; } Node prevNode = removeNode.prev; Node nextNode = removeNode.next; prevNode.next = nextNode; nextNode.prev = prevNode; }
然后我们测试一下:
直接调用removeByValue即可
@Test public void test3() { doubleRingLinked.addLast(1); doubleRingLinked.addLast(2); doubleRingLinked.addLast(3); doubleRingLinked.addLast(4); doubleRingLinked.addLast(5); doubleRingLinked.removeByValue(4); for (Integer value: doubleRingLinked) { System.out.println(value); } }
结果:
1 2 3 5