1. 程式人生 > >java資料結構——單向連結串列

java資料結構——單向連結串列

連結串列是非常常見的一類線性結構的資料結構,每個節點包含有指標域和資料域,常見的包括單項列表、雙向列表、迴圈列表。這篇文章將詳細介紹單向連結串列。

單向連結串列每個節點包含當前節點的資料域和一個指向下一個節點的指標域,如下:

本文將介紹連結串列的尾節點插入、頭結點插入、指定位置插入、刪除頭結點、刪除尾節點、刪除指定節點、刪除指定元素,連結串列反轉、連結串列是否為空、連結串列長度、獲取頭結點、獲取尾節點。

連結串列的節點表示:

public class Node {
    //下一個節點
    public Node next;
    //當前節點的資料
    public String data;

    public Node(String data) {
        this.data = data;
        this.next = null;
    }
}

在建立好連結串列節點之後下來看連結串列的構造方法和成員變數,成員方法包括一個完整連結串列的頭結點、尾節點以及連結串列的長度:

public class SingleLinkedList {

    //頭結點
    private Node head;
    //尾節點
    private Node tail;
    //長度
    private int length;

    public SingleLinkedList() {
        this.head = null;
        this.tail = null;
    }
}

1、插入元素:尾部插入

    /**
     * 插入資料
     */
    public Node insert(String data) {
        //新節點
        Node node = new Node(data);
        if (isEmpty()) {
            //首次插入
            head = node;
            tail = node;
        } else {
            //非首次插入
            tail.next = node;
            tail = node;
        }
        length++;
        return node;
    }

2、插入元素:頭部插入

    /**
     * 頭部插入元素
     */
    public Node insertHead(String data) {
        Node node = new Node(data);
        Node lastNode;
        lastNode = head;
        head = node;
        head.next = lastNode;
        length++;
        return node;
    }

3、插入元素:指定位置

    /**
     * 指定位置插入
     */
    public Node insert(String data, int position) {
        if (position < 0) {
            throw new IndexOutOfBoundsException();
        }
        //新節點
        Node node = new Node(data);

        if (position == 0) {
            //插入頭部
            insertHead(data);
        } else if (isEmpty() || position >= length()) {
            //插入尾部
            insert(data);
        } else {
            //插入中間
            //node上一個元素
            Node current = head;
            //node的下一個元素
            Node nextNode = current.next;
            for (int i = 1; i < length(); i++) {
                if (i == position) {
                    //上一個元素指向node
                    current.next = node;
                    //node下一個元素指向原始current的下一個
                    node.next = nextNode;
                    break;
                } else {
                    //更新下一個節點
                    current = current.next;
                    nextNode = current.next;
                }
            }
            length++;
        }
        return node;
    }

4、刪除元素:從頭結點刪除

    /**
     * 刪除元素:從尾節點刪除
     */
    public Node deleteTail() {
        Node deleteNode = tail;
        if (isEmpty()) {
            //沒有元素。刪除失敗,丟擲異常
            throw new RuntimeException("please insert element first");
        }
        if (length == 1) {
            //只有一個元素,頭尾元素相同,直接刪除
            head = null;
            tail = null;
        } else {
            //含義多個元素
            Node lastNode = head;
            //lastNode.next不會空指標就執行完畢,以為tail存在
            while (lastNode.next != tail) {
                lastNode = lastNode.next;
            }
            tail = lastNode;
            tail.next = null;
        }
        length--;
        return deleteNode;
    }

5、刪除元素:指定位置刪除

    /**
     * 從指定位置刪除
     */
    public Node delete(int position) {
        if (isEmpty()) {
            //沒有元素。刪除失敗,丟擲異常
            throw new RuntimeException("please insert element first");
        }
        if (position < 0 || position > length() - 1) {
            //下標越界
            throw new IndexOutOfBoundsException();
        }
        if (position == 0) {
            //刪除頭部
            return deleteHead();
        } else if (position == length() - 1) {
            //刪除尾部
            return deleteTail();
        } else {
            //刪除中間,至少有3個元素
            //上一個元素
            Node lastNode = head;
            //當前元素
            Node currentNode = lastNode.next;
            //下一個元素
            Node nextNode = currentNode.next;

            for (int i = 1; i < length(); i++) {
                if (i == position) {
                    //上一個元素指向node
                    lastNode.next = nextNode;
                    break;
                } else {
                    //更新下一個節點
                    lastNode = currentNode;
                    currentNode = nextNode;
                    nextNode = nextNode.next;
                }
            }
            length--;
            return currentNode;
        }
    }

6、刪除元素:指定元素

    /**
     * 刪除指定元素
     */
    public String delete(String data) {

        if (data == null) {
            throw new NullPointerException();
        }
        if (isEmpty()) {
            return null;
        }

        if (head.data.equals(data)) {
            //頭結點
            deleteHead();
            return data;
        } else if (tail.data.equals(data)) {
            deleteTail();
            return data;
        } else {
            //刪除中間的
            //上一個元素
            Node lastNode = null;
            //當前元素
            Node currentNode = head;
            //下一個元素
            Node nextNode = currentNode.next;
            while (currentNode != null) {
                if (currentNode.data.equals(data)) {
                    lastNode.next = nextNode;
                    length--;
                    return data;
                }
                if (nextNode == null) {
                    return null;
                } else {
                    lastNode = currentNode;
                    currentNode = nextNode;
                    nextNode = nextNode.next;
                }
            }
        }
        return null;
    }

7、連結串列反轉:迭代法

其他的連結串列操作演算法都不算太難,只要想通了寫起來除錯除錯執行起來應該沒問題,但是連結串列的反轉需要一定的技巧了,由於頭結點指向下一個引用,所以頭結點是個關鍵,在連結串列的反轉中通常有兩種方法一種是迭代法,另一種是遞迴法,下面詳細介紹迭代法的執行步驟,以連結串列資料1234為例,反轉後的值為4321,初始結構如下:

建立三個變數第一個指向當一個節點,第二個指向當前節點,第三個指向下一個節點,使用while迴圈,每迴圈一次,向下移動一個位置,直到當前的節點為null跳出迴圈。

第一步:將第二個元素的指標域指向上一個,即當前節點為2,上一個節點為1,下一個節點為3,2指向1

第二步:進行下一次迴圈,向下走一步,當前當前節點為3,上一個節點為2,下一個節點為4,3指向2

第三步:再進行一次迴圈,以此類推,4節點指向3節點……

最後:將頭結點和第二個節點之間的鏈斷開

形成最終反轉的連結串列如下:

通用演算法表示如下:

    /**
     * 連結串列的反轉:迭代法
     */
    public Node revirse(Node head) {
        tail = head;
        if (isEmpty()) {
            System.out.println("沒有可以反轉的連結串列");
        }
        if (head.next == null) {
            return head;
        }
        //反轉後的節點
        Node reNode = revirse(head.next);
        head.next.next = head;
        head.next = null;
        return reNode;
    }

8、連結串列反轉:遞迴法

    /**
     * 連結串列的反轉:迭代法
     */
    public Node revirse(Node head) {
        tail = head;
        if (isEmpty()) {
            System.out.println("沒有可以反轉的連結串列");
        }
        if (head.next == null) {
            return head;
        }
        //反轉後的節點
        Node reNode = revirse(head.next);
        head.next.next = head;
        head.next = null;
        return reNode;
    }

9、檢視頭結點

    /**
     * 檢視頭結點
     */
    public Node getHead() {
        return head;
    }

10、檢視尾節點

    /**
     * 檢視尾節點
     */
    public Node getTail() {
        return tail;
    }

11、檢視所有節點

    /**
     * 檢視所有節點
     */
    public void display() {
        if (isEmpty()) {
            System.out.println("連結串列為空,請先插入元素");
            return;
        }

        Node current = head;
        while (current != null) {
            System.out.print(current.data + " ");
            current = current.next;
        }
        System.out.println();
    }

12、節點是否為空

    /**
     * 檢視節點是否為空
     */
    public boolean isEmpty() {
        return length == 0;
    }

13、檢視連結串列長度

    /**
     * 檢視連結串列長度
     */
    public int length() {
        return length;
    }

介紹完畢,關於雙端連結串列看下一篇介紹!