循環鏈表(單鏈表)


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

   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM