03數據結構和算法(Java描述)~單鏈表

03數據結構和算法(Java描述)~單鏈表

本文是上一篇文章的後續,詳情點擊該鏈接

鏈表

單鏈表

單鏈表的定義

       單鏈表採用的是鏈式存儲結構,使用一組地址任意的存儲單元來存放數據元素。在單鏈表中, 存儲的每一條數據都是以節點來表示的,每個節點的構成爲:元素(存儲數據的存儲單元) + 指 針(存儲下一個節點的地址值),單鏈表的節點結構如下圖所示:

在這裏插入圖片描述

       另外,單鏈表中的開始節點,我們又稱之爲首節點;單鏈表中的終端節點,我們又稱之爲尾節 點。如下圖所示:

在這裏插入圖片描述

根據序號獲取結點的操作:

       在線性表中,每個節點都有一個唯一的序號,該序號是從 0開始遞增的。通過序號獲取單鏈表 的節點時,我們需要從單鏈表的首節點開始,從前往後循環遍歷,直到遇到查詢序號所對應的節點 時爲止。

       以下圖爲例,我們需要獲得序號爲2的節點,那麼就需要依次遍歷獲得“節點 11”和“節點 22”, 然後才能獲得序號爲 2的節點,也就是“節點 33”。

在這裏插入圖片描述

       因此,在鏈表中通過序號獲得節點的操作效率是非常低的,查詢的時間複雜度爲 O(n)。

根據序號刪除節點的操作

       根據序號刪除節點的操作,我們首先應該根據序號獲得需要刪除的節點,然後讓“刪除節點的 前一個節點”指向“刪除節點的後一個節點”,這樣就實現了節點的刪除操作。

       以下圖爲例,我們需要刪除序號爲2的節點,那麼就讓“節點22”指向“節點44”即可,這樣 就刪除了序號爲 2的節點,也就是刪除了“節點 33”。

在這裏插入圖片描述

       通過序號來插入節點,時間主要浪費在找正確的刪除位置上,故時間複雜度爲 O(n)。但是,單 論刪除的操作,也就是無需考慮定位到刪除節點的位置,那麼刪除操作的時間複雜度就是 O(1)。

根據序號插入節點的操作

       根據序號插入節點的操作,我們首先應該根據序號找到插入的節點位置,然後讓“插入位置的 上一個節點”指向“新插入的節點”,然後再讓“新插入的節點”指向“插入位置的節點”,這樣 就實現了節點的插入操作。

       以下圖爲例,我們需要在序號爲 2 的位置插入元素值“00”,首先先把字符串“00”封裝爲一 個節點對象,然後就讓“節點 22”指向“新節點 00”,最後再讓“節點00”指向“節點 33”,這 樣就插入了一個新節點。

在這裏插入圖片描述

       通過序號來插入節點,時間主要浪費在找正確的插入位置上,故時間複雜度爲 O(n)。但是,單 論插入的操作,也就是無需考慮定位到插入節點的位置,那麼插入操作的時間複雜度就是 O(1)。

順序表和單鏈表的比較

在這裏插入圖片描述

存儲方式比較

       順序表採用一組地址連續的存儲單元依次存放數據元素,通過元素之間的先後順序來確定元素 之間的位置,因此存儲空間的利用率較高

       單鏈表採用一組地址任意的存儲單元來存放數據元素,通過存儲下一個節點的地址值來確定節 點之間的位置,因此存儲空間的利用率較低。

       順序表查找的時間複雜度爲 O(1),插入和刪除需要移動元素,因此時間複雜度爲 O(n)。若是需 要頻繁的執行查找操作,但是很少進行插入和刪除操作,那麼建議使用順序表。

       單鏈表查找的時間複雜度爲 O(n),插入和刪除無需移動元素,因此時間複雜度爲 O(1)。若是需 要頻繁的執行插入和刪除操作,但是很少進行查找操作,那麼建議使用鏈表。

       補充:根據序號來插入和刪除節點,需要通過序號來找到插入和刪除節點的位置,那麼整體的 時間複雜度爲 O(n)。因此,單鏈表適合數據量較小時的插入和刪除操作,如果存儲的數據量較大, 那麼就建議使用別的數據結構,例如使用二叉樹來實現。

空間性能比較

       順序表需要預先分配一定長度的存儲空間,如果事先不知道需要存儲元素的個數,分配空間過 大就會造成存儲空間的浪費,分配空間過小則需要執行耗時的擴容操作。

       單鏈表不需要固定長度的存儲空間,可根據需求來進行臨時分配,只要有內存足夠就可以分配, 在鏈表中存儲元素的個數是沒有限制的,無需考慮擴容操作。

代碼實現

定義List接口
public interface List {
    int size();
    void add(Object element);
    Object get(int index);
    void remove(int index);
    void add(int index, Object element);
    String toString();
}
SingleLinkedList實現類
public class SingleLinkedList implements List{
     // 用於保存單鏈表中的首節點
    private Node headNode;

    // 用於保存單鏈表中的尾節點
    private Node lastNode;

   // 用於保存單鏈表中節點的個數
    private int size;

     // 獲取單鏈表中節點的個數
    public int size() {
        return this.size;
    }
    /**
     * 添加元素
     * @param element 需要添加的數據
     */
    public void add(Object element) {
        // 1.把需要添加的數據封裝成節點對象
        Node node = new Node(element);
        // 2.處理單鏈表爲空表的情況
        if(headNode == null) {
            // 2.1把node節點設置爲單鏈表的首節點
            headNode = node;
            // 2.2把node節點設置爲單鏈表的尾節點
            lastNode = node;
        }
        // 3.處理單鏈表不是空表的情況
        else {
            // 3.1讓lastNode指向node節點
            lastNode.next = node;
            // 3.2更新lastNode的值
            lastNode = node;
        }
        // 4.更新size的值
        size++;
    }

    /**
     * 根據序號獲取元素
     * @param index 序號
     * @return 序號所對應節點的數據值
     */
    public Object get(int index) {
        // 1.判斷序號是否合法,合法取值範圍:[0, size - 1]
        if(index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("序號不合法,index:" + index);
        }
        // 2.根據序號獲得對應的節點對象
        Node node = node(index);
        // 3.獲取並返回node節點的數據值
        return node.data;
    }

    /**
     * 根據序號刪除元素
     * @param index 序號
     */
    public void remove(int index) {
        // 1.判斷序號是否合法,合法取值範圍:[0, size - 1]
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException("序號不合法,index:" + index);
        }
        // 2.處理刪除節點在開頭的情況
        if (index == 0) {
            // 2.1獲得刪除節點的後一個節點
            Node nextNode = headNode.next;
            // 2.2設置headNode的next值爲null
            headNode.next = null;
            // 2.3設置nextNode爲單鏈表的首節點
            headNode = nextNode;
        }
        // 3.處理刪除節點在末尾的情況
        else if (index == size - 1) {
            // 3.1獲得刪除節點的前一個節點
            Node preNode = node(index - 1);
            // 3.2設置preNode的next值爲null
            preNode.next = null;
            // 3.3設置preNode爲單鏈表的尾節點
            lastNode = preNode;
        }
        // 4.處理刪除節點在中間的情況
        else {
            // 4.1獲得index-1所對應的節點對象
            Node preNode = node(index - 1);
            // 4.2獲得index+1所對應的節點對象
            Node nextNode = preNode.next.next;
            // 4.3獲得刪除節點並設置next值爲null
            preNode.next.next = null;
            // 4.4設置preNode的next值爲nextNode
            preNode.next = nextNode;
        }
        // 5.更新size的值
        size--;
    }

    /**
     * 根據序號插入元素
     * @param index 序號
     * @param element 需要插入的數據
     */
    public void add(int index, Object element) {
        // 1.判斷序號是否合法,合法取值範圍:[0, size]
        if(index < 0 || index > size) {
            throw new IndexOutOfBoundsException("序號不合法,index:" + index);
        }
        // 2.把需要添加的數據封裝成節點對象
        Node node = new Node(element);
        // 3.處理插入節點在開頭位置的情況
        if(index == 0) {
            // 3.1設置node的next值爲headNode
            node.next = headNode;
            // 3.2設置node節點爲單鏈表的首節點
            headNode = node;
        }
        // 4.處理插入節點在末尾位置的情況
        else if(index == size) {
            // 4.1設置lastNode的next值爲node
            lastNode.next = node;
            // 4.2設置node節點爲單鏈表的尾節點
            lastNode = node;
        }
        // 5.處理插入節點在中間位置的情況
        else {
            // 5.1獲得index-1所對應的節點對象
            Node preNode = node(index - 1);
            // 5.2獲得index所對應的節點對象
            Node curNode = preNode.next;
            // 5.3設置preNode的next爲node
            preNode.next = node;
            // 5.4設置node的next爲curNode
            node.next = curNode;
        }
        // 6.更新size的值
        size++;
    }

    /**
     * 根據序號獲得對應的節點對象
     * @param index 序號
     * @return 序號對應的節點對象
     */
    private Node node(int index) {
        // 1.定義一個零時節點,用於輔助單鏈表的遍歷操作
        Node tempNode = headNode;
        // 2.定義一個循環,用於獲取index對應的節點對象
        for(int i = 0; i < index; i++) {
            // 3.更新tempNode的值
            tempNode = tempNode.next;
        }
        // 4.返回index對應的節點對象
        return tempNode;
    }

  
    // 節點類

    private static class Node {
        /**
         * 用於保存節點中的數據
         */
        private Object data;
        /**
         * 用於保存指向下一個節點的地址值
         */
        private Node next;
        /**
         * 構造方法
         * @param data
         */
        public Node(Object data) {
            this.data = data;
        }
    }

    public String toString() {
        //判斷是否爲空,如果爲空就直接返回 []
        if(headNode == null){
            return "[]";
        }
        StringBuilder stringBuilder = new StringBuilder("[");
        Node p = headNode.next;
        while(p != null){
            stringBuilder.append(p.data + ",");
            p = p.next;
        }
        //最後一個逗號刪掉
        stringBuilder.deleteCharAt(stringBuilder.length()-1);
        stringBuilder.append("]");
        return stringBuilder.toString();
    }
}
Test
public class Test {
    public static void main(String[] args) {
        // 1.創建一個對象
        List list = new SingleLinkedList();
        // 2.添加元素
        list.add("11"); // 0
        list.add("22"); // 1
        list.add("33"); // 2
        list.add("44"); // 3
        list.add("55"); // 4
        //刪除
        list.remove(0);
        //序號添加
        list.add(2, "00");
        //測試get
        for(int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
        //toString
        System.out.println(list.toString());
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章