1. 程式人生 > >JAVA實現單鏈表的增刪改查

JAVA實現單鏈表的增刪改查

前言

實現參考:

建議閱讀以上部落格,我的部落格更偏向於[我]能看懂
上面的部落格則偏向於[大家]都能看懂

實現的功能

增加

  • 指定位置插入節點
  • 在末尾增加節點

刪除

  • 刪除指定節點,通過節點資料(假設資料不重複)
  • 刪除指定位置節點

修改

  • 修改指定位置節點資料

查詢

  • 查詢某個節點的位置
  • 查詢某個位置中的節點資料
  • 查詢某個資料是否存在於連結串列中(假設資料不重複)

轉陣列

  • 將連結串列轉成陣列

中文版虛擬碼

/**
 *
 * 單向連結串列
 *      節點:
 *          資料都存放在一個節點中
 *          每個節點都有指向下一個節點的指標
 *      頭指標:
 *          連結串列中的頭個節點,不計入節點數,表示連結串列頭
 *      當前指標:
 *        在連結串列中游走,指向哪,跑到哪。
 * 增加
 *      新增節點到尾部時
 *          空連結串列:新增的節點成為頭指標的next指向的節點
 *          非空連結串列:成為最後一個節點的下一個節點,更新當前指標為下一個節點
 *      新增節點到第n個位置時
 *          如果插入的位置不是頭:
 *              到達第n-1個節點
 *              新節點指向第n-1個的next節點
 *              第n-1個節點的next指向新節點
 *          如果是第一個位置(頭):
 *              新節點的next指向頭指標的節點
 *              更新頭指標的next指向新節點
 * 刪除
 *      根據資料刪除
 *          判斷資料是否存在或者是否為空
 *              如果存在,且位置為n
 *                  將當前指標指向第n-1個節點
 *                  更新第n-1個節點的next指向第n+1個節點
 *              否則
 *                  丟擲異常
 *      根據傳入的位置n刪除
 *          判斷引數n是否合法   (n >= 0 && n < size)
 *              若合法
 *                  將當前指標指向第n-1個節點
 *                  更新第n-1個節點的next指向第n+1個節點
 *               否則
 *                  丟擲異常
 *
 * 查詢
 *      根據資料查詢節點位置
 *          判斷輸入資料的合法性
 *          合法
 *              遍歷查詢
 *              找到,返回位置
 *              沒找到,返回-1
 *      獲取準確資料,根據傳入位置n查詢
 *          判斷輸入資料的合法性
 *              定位到n,返回資料
 *      存在性查詢,根據傳入資料查詢連結串列中是否存在這樣一個節點
 *          判斷輸入資料的合法性
 *              遍歷查詢
 *              存在,返回真
 *              不存在,返回假
 * 修改
 *      傳入資料和位置
 *      驗證位置的合法性
 *      合法
 *          當前指標跳轉到目標節點修改
 *          返回真
 *      不合法
 *          返回假
 */

具體實現

public class SingleLinkedList<T> {

    private Node headNode;
    private Node currNode;
    private int size;

    SingleLinkedList() {
        headNode = new Node();
        currNode = null;
        size = 0;
    }

    public int Size() {
        return size;
    }

    //新增節點到連結串列尾部
public void insert(T data) { Node tNode = new Node(data); if (isEmpty()) { headNode.setNext(tNode); } else { toIndexOf(size - 1); currNode.setNext(tNode); } size++; } //新增節點到第n個位置 public void insert(T data, int
n) { //將當前指標定位到第n-1個節點 toIndexOf(n - 1); //如果為空,插入到第一個節點 if (isEmpty()) { insert(data); return; } // 將新節點的next指向第n個節點, 第n-1個節點的next指向新節點 // 即新節點為第n個,原第n個節點為n+1 currNode.setNext(new Node(data, currNode.getNext())); size++; } //查詢第n個節點的資料 public <T> T get(int n) { //當前指標移動到第n個節點 toIndexOf(n); return (T) currNode.getData(); } //查詢某節點的位置 public int getElemAt(T data) { if (data == null) { throw new NullPointerException("既然喜歡null,那就給你咯。"); } currNode = headNode.getNext(); int i = 0; while (currNode != null) { if (currNode.getData().equals(data)) { return i; } i++; currNode = currNode.getNext(); } return -1; } //修改節點的資料 public boolean setElemAt(T data, int n) { toIndexOf(n); currNode.setData(data); return true; } //查詢某節點是否存在 public boolean contains(T data) { return getElemAt(data) == -1 ? false : true; } //刪除第n個節點 public T delete(int n) { toIndexOf(n - 1); T data = currNode.getNext().getData(); currNode.setNext(currNode.getNext().getNext()); size--; return data; } //刪除指定節點 public T deleteElemBy(T data) { int n = getElemAt(data); return delete(n); } public boolean isEmpty() { return size == 0 ? true : false; } //將其轉為陣列 public Object[] toArray() { if (isEmpty()) { throw new IndexOutOfBoundsException("Size: " + size); } Object[] arr = new Object[size]; toIndexOf(0); int i = 0; while (currNode != null) { arr[i++] = currNode.getData(); currNode = currNode.getNext(); } return arr; } //將當前指標移動到指定位置 private void toIndexOf(int n) { if (isEmpty() || !isIndexOk(n)) { throw new IndexOutOfBoundsException("Index: " + n + ", Size: " + size); } currNode = headNode; if (n == -1) return; while (n != -1) { currNode = currNode.getNext(); n--; } } private boolean isIndexOk(int n) { if (n < -1 || n >= size) return false; return true; } private class Node { private T data; private Node next; Node() { } Node(T data) { this.data = data; } Node(T data, Node nextNode) { this.data = data; this.next = nextNode; } public T getData() { return data; } public Node getNext() { return next; } public void setData(T data) { this.data = data; } public void setNext(Node next) { this.next = next; } } }

測試

public class LinkedListTest {
    public static void main(String[] args) {
        SingleLinkedList<Integer> test = new SingleLinkedList<Integer>();
        for (int i = 0; i < 10; i++) {
            test.insert(i);
        }

        //System.out.println("------------越界測試--------------");
        //System.out.println(test.get(-1));
        //System.out.println(test.get(test.Size()));
        System.out.println("角標讀取:");
        System.out.print(test.get(0) + ",");
        System.out.print(test.get(4) + ",");
        System.out.println(test.get(test.Size() - 1));

        System.out.println("------------轉換陣列--------------");
        Object[] data = test.toArray();
        showArray(data);


        System.out.println("------------根據元素獲取角標--------------");
        int el1 = 2, el2 = 22;
        System.out.println(el1 + " at " + test.getElemAt(2));
        System.out.println(el2 + " at " + test.getElemAt(22));

        System.out.println("------------原陣列--------------");
        data = test.toArray();
        showArray(data);
        System.out.println("角標刪除測試,刪除");
        System.out.print(test.delete(9) + ",");
        System.out.print(test.delete(0) + ",");
        System.out.println(test.delete(3));
        System.out.println("刪除完畢");
        data = test.toArray();
        showArray(data);

        System.out.println("元素刪除測試,刪除:");
        System.out.print(test.deleteElemBy(1) + ",");
        System.out.print(test.deleteElemBy(8) + ",");
        System.out.println(test.deleteElemBy(5));
        System.out.println("刪除完畢:");
        data = test.toArray();
        showArray(data);
        System.out.println("------------包含測試--------------");
        data = test.toArray();
        showArray(data);
        int n1 = 6, n2 = 10;
        System.out.println(n1 + ",在嗎?" + test.contains(n1));
        System.out.println(n2 + ",在嗎?" + test.contains(n2));

        System.out.println("------------插入測試--------------");
        data = test.toArray();
        showArray(data);
        test.insert(4, 2);
        test.insert(5, 3);
        System.out.println("插入4、5分別到第2、3個位置,完成:");
        data = test.toArray();
        showArray(data);

        System.out.println("------------修改測試--------------");
        data = test.toArray();
        showArray(data);
        test.setElemAt(1024, 2);
        test.insert(2048, 3);
        System.out.println("修改角標2、3分別到為1024、2048,完成:");
        data = test.toArray();
        showArray(data);
        test.setElemAt(6, 10);
    }

    public static void showArray(Object[] data) {
        int len = data.length - 1;
        for (int i = 0; i <= len; i++) {
            System.out.print(data[i]);
            System.out.print(i < len ? " " : "");
        }
        System.out.println();
    }
}

結果

角標讀取:
0,4,9
------------轉換陣列--------------
0 1 2 3 4 5 6 7 8 9
------------根據元素獲取角標--------------
2 at 2
22 at -1
------------原陣列--------------
0 1 2 3 4 5 6 7 8 9
角標刪除測試,刪除
9,0,4
刪除完畢
1 2 3 5 6 7 8
元素刪除測試,刪除:
1,8,5
刪除完畢:
2 3 6 7
------------包含測試--------------
2 3 6 7
6,在嗎?true
10,在嗎?false
------------插入測試--------------
2 3 6 7
插入45分別到第23個位置,完成:
2 3 4 5 6 7
------------修改測試--------------
2 3 4 5 6 7
修改角標23分別到為10242048,完成:
2 3 1024 2048 5 6 7
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 10, Size: 7
    at SingleLinkedList.toIndexOf(SingleLinkedList.java:183)
    at SingleLinkedList.setElemAt(SingleLinkedList.java:135)
    at LinkedListTest.main(LinkedListTest.java:68)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

收穫

第一次用泛型(大概是亂用)

靜態內部類和非靜態內部類的區別
靜態內部類不能訪問外部類的非靜態成員
類似於把靜態內部類獨立了

意識到連結串列的各種操作十分耗時!!查詢一個,查一個就要迴圈遍歷一遍也是沒誰了。

腦海裡多了個概念,一個類似遊標指標的概念,指定它去哪個位置就去哪個位置獲取元素,不知道叫遊標合適不合適,總之遊標很忙。在我的實現裡的currNode就是經常跑頭跑尾。

把連結串列操作完轉成陣列會提高效率,也許?

發現自己對異常不熟悉到大腦一片空白

END