1. 程式人生 > >使用Java實現單向連結串列,並完成連結串列反轉。

使用Java實現單向連結串列,並完成連結串列反轉。

使用Java實現單向連結串列,並完成連結串列反轉。

演算法和資料結構是程式設計師逃不過的一個坎,所以趁著閒餘時間,開始學習基礎的演算法和資料結構。這裡記錄下自己實現簡單的單項鍊表的過程,如有錯誤,敬請指正。

明確需求

在Java中,常用的資料容器裡面,跟連結串列關係緊密的當屬LinkedList了,它的底層實現為雙向連結串列,這裡就以它為參照物,實現自己的簡單的單向連結串列。另外,還需要支援增刪改查、獲取大小等功能。
如下所示,先定義了增刪改查方法支援的範圍。

    /**
     * 增刪改查方法
     * add(index, E)        index >= 0 && index <= size
     * remove(index)        index >= 0 && index < size
     * set(index, E)        index >= 0 && index < size
     * get(index)           index >= 0 && index < size
     */

定義連結串列結點Node

結點的定義很簡單,這裡為了簡單起見,不支援泛型,結點儲存的資料型別固定為int。

    private static class Node {
        private int item;
        private Node next;

        public Node(int item, Node next) {
            this.item = item;
            this.next = next;
        }

        public void setItem(int item) {
            this.item = item;
        }

        @Override
        public String toString() {
            return String.valueOf(this.item);
        }
    }

定義輔助方法

既然是單向連結串列,容器中只需要持有first引用即可,不需要last引用。再需要定義一個size用來表示連結串列大小;另外,為了方便檢視測試結果,我們重寫toString方法。程式碼如下:

public class SingleLinkedList {

    private int size = 0;
    private Node first = null;
    ...
}
    /**
     * 返回當前容器大小。
     * @return
     */
    public int getSize() {
        return size;
    }
    
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        if (first != null) {
            Node curr = first;
            while (curr != null) {
                sb.append(String.valueOf(curr));
                if (curr.next != null) {
                    sb.append(",").append(" ");
                }
                curr = curr.next;
            }
        }
        sb.append("]");
        return sb.toString();
    }

定義查詢方法get(index)

在已有資料中獲取資料,這個最為簡單,由於單向連結串列容器中只儲存首個結點first引用,所以不能直接角標index獲取,需要從first中根據角標依次遍歷,程式碼如下:

    /**
     * 獲取角標上的資料。
     *
     * @param index
     * @return
     */
    public Node get(int index) {
        checkElementIndex(index);

        Node x = first;
        for (int i = 0; i < index; i++) {
            x = x.next;
        }
        return x;
    }

定義增加方法add(index, value)

與另外三組方法(獲取、修改、刪除)均不同,增加方法不僅僅要支援連結串列現有的資料範圍,而且還需要支援角標為size的位置。
在當前位置插入結點,需要獲取到前一個結點,讓前一個結點的next指向新增的結點,再讓新增結點的next指向該位置原來的結點。需要注意一些特殊情況的處理,還有不要忘了size的數值增加。程式碼如下:

    /**
     * 在角標為index的位置插入資料
     *
     * @param index
     * @param value
     * @return
     */
    public boolean add(int index, int value) {
        checkPositionIndex(index);

        if (index == 0) {
            if (first == null) {
                first = new Node(value, null);
            } else {
                Node next = get(0);
                Node node = new Node(value, next);
                first = node;
            }
        } else {
            Node prev = get(index - 1);
            Node node = new Node(value, prev.next);
            prev.next = node;
        }
        size++;
        return true;
    }

定義修改方法set(index, value)

修改方法也很簡單,只需根據角標獲取已經存在結點,然後將結點中儲存的資料修改即可,程式碼如下:

    public boolean set(int index, int value) {
        checkElementIndex(index);
        Node node = get(index);
        node.setItem(value);
        return true;
    }

定義刪除方法remove(index)

刪除當前位置的結點,核心是讓上一個結點的next指向當前位置的下一個結點即可,與新增方法類似,需要做好極端情況的處理,程式碼如下:

    public boolean remove(int index) {
        checkElementIndex(index);

        // size > 0
        if (getSize() == 1) {//size == 1
            first = null;
        } else {// size > 1
            if (index == 0) {// 刪除第一個資料
                first = first.next;
            } else if (getSize() - 1 == index) {// 刪除最後一個數據
                Node prev = get(index - 1);
                prev.next = null;
            } else {// 刪除中間的資料
                get(index - 1).next = get(index).next;
            }
        }
        size--;
        return true;
    }

實現連結串列反轉

為了實現單向連結串列的反轉,在遍歷的過程中,讓每個節點的next指向上一個結點。程式碼如下:

    /**
     * 反轉連結串列
     *
     * @return
     */
    public Node reverseList() {
        Node prev = null;
        Node curr = first;
        while (curr != null) {
            Node temp = curr.next;
            curr.next = prev;
            prev = curr;
            curr = temp;
        }
        logFromHead("reverseList", prev);
        return prev;
    }

完整程式碼請檢視

專案中搜索SingleLinkedList即可。
github傳送門 https://github.com/tinyvampirepudge/DataStructureDemo

gitee傳送門 https://gitee.com/tinytongtong/DataStructureDemo

參考

https://leetcode.com/problems/reverse-linked-list/description/