1. 程式人生 > 其它 >迴圈連結串列(單鏈表)

迴圈連結串列(單鏈表)

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