在單鏈表中,尾節點的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(); }