迴圈連結串列(單鏈表)
在單鏈表中,尾節點的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(); }