在单链表中,尾节点的next指向null,如果尾节点的next指向头节点,链表不就循环起来了?在循环链表中,没有一个节点的next指向null。尽管每一个节点都指向下一个节点,但循环链表还是有头部和尾部之分。外部怎么访问循环链表?需要一个外部的引用指向链表,那指向链表的头节点还是尾节点?指向链表的尾节点。因为如果指向头节点的话,链表尾部的插入需要遍历整个链表,指向尾节点不存在这个问题。如果在头部插入,循环列表尾节点的next就是头节点,也没有问题,循环列表有如下几种情况
当循环链表中只有一个节点时,节点自己指向自己。实现循环链表,需要一个外部变量指向链表的尾节点。
class CircleLinkedList<T> { private class Node { T data; Node next; Node(T data) { this.data = data; } } private Node last; }
向链表中插入元素有三种情况,插入到链表的头部,插入到链表的尾部,插入到链表两个节点之间。当然,无论是哪种情况,都要检查链表是否为空。如果链表为空,让last指向新创建的节点,并自己指向自己。
private void addToEmpty(T data) { var newNode = new Node(data); last = newNode; newNode.next =last; // 链表自己指向自己 }
如果链表不为空,比如有如下链表
1,向链表头部插入元素,比如向链表的头部插入6,那就先创建一个新节点newNode, 然后让newNode.next 指向last.next,因为last.next就是头节点,最后让last.next指向最新的头节点newNode.
public void addFront(T data) { if (last == null) { addToEmpty(data); } else { var newNode = new Node(data); newNode.next = last.next; last.next = newNode; } }
2,向链表尾部插入节点,让last的next指向新的节点,last再指向新节点,当然,不要忘记它是循环链表,新节点的next还要指向头节点
public void addLast(T data) { if (last == null) { addToEmpty(data); } else { var newNode = new Node(data); newEntry.next = last.next; // 新节点的next指向头节点 last.next = newEntry; last = newEntry; } }
3,向两个节点之间添加元素,就是向某一个元素后面添加元素,首先遍历链表,找到这个元素。如果这个元素正好是尾节点,那么还要移动last的位置
// 向某个item后面 添加值, public void addAfter(T item, T data) { if(last == null) { throw new RuntimeException("链表为空"); } else { var found = false; var temp = last.next; // 头节点 do { if(temp.data.equals(item)){ found = true; } else { temp = temp.next; } } while(temp != last.next && !found); if(found) { // temp就是将要向它向面添加元素的节点 var newNode = new Node(data); // 先获取到旧的temp.next var oldNext = temp.next; temp.next = newNode; newNode.next = oldNext; //如果正好添加尾节点后面,还要移动last的指向 if (temp == last) { last = newNode; } } else { throw new RuntimeException("没有找到元素"); } } }
删除节点:如果链表为空,肯定是要报错的。如果链表中只有一个节点,那么如果删除的节点正好是这个节点,那么就删除,last=null,如果不是,那也只能报错了。如果有多个节点,但正好删除的是最后一个节点,那就要循环链表,找到最后一个节点的前一个节点,然后修改last指向,
如果是删除中间一个元素,遍历找到前一个节点,
public void deleteNode(T item) { // 链表为空,肯定不能删除 if(last == null) { throw new RuntimeException("链表为空"); } else if(last.next == last) { // 链表只有一个节点 if (last.data.equals(item)){ last = null; } else { throw new RuntimeException("没有找到元素"); } // 链表有多个节点 } else if (last.data.equals(item)) { // 删除的节点,正好是最后一个节点 // 找到最后一个节点的前一个节点 var temp = last; while(temp.next != last) { temp = temp.next; } temp.next = last.next; last = temp; } else { var temp = last; // 找到前面一个节点 while(!temp.next.data.equals(item) && temp.next != last) { temp = temp.next; } if(temp.next.data.equals(item)) { temp.next= temp.next.next; } else { throw new RuntimeException("没有找到元素"); } } }
遍历
public void traverse() { if (last == null) { throw new RuntimeExceiption('空链表'); } var p = last.next; do { System.out.print(p.data + " "); p = p.next; } while (p != last.next); }
测试
public static void main(String[] args) { var circleList = new CircleSingleLinkedList<Integer>(); circleList.addFront(6); circleList.addEnd(8); circleList.addFront(2); circleList.addAfter(2, 10); circleList.traverse(); System.out.println(); circleList.deleteNode(8); circleList.traverse(); }