什麼是鏈表?

来源:https://www.cnblogs.com/wupeixuan/archive/2020/02/09/12285989.html
-Advertisement-
Play Games

在瞭解完 "什麼是數據結構" 之後,讓我們一起來探索下數據結構中常見的一種— 鏈表 。 鏈表 鏈表是數據結構之一, 其中的數據呈線性排列。在鏈表中,數據的添加和刪除都較為方便,就是訪問比較耗費時間。 如上圖所示就是鏈表的概念圖,Blue、Yellow、Red 這 3 個字元串作為數據被存儲於鏈表中, ...


在瞭解完什麼是數據結構之後,讓我們一起來探索下數據結構中常見的一種—鏈表

鏈表

鏈表是數據結構之一,其中的數據呈線性排列。在鏈表中,數據的添加和刪除都較為方便,就是訪問比較耗費時間。

如上圖所示就是鏈表的概念圖,Blue、Yellow、Red 這 3 個字元串作為數據被存儲於鏈表中,也就是數據域,每個數據都有 1 個指針,即指針域,它指向下一個數據的記憶體地址,其中 Red 是最後 1 個數據,Red 的指針不指向任何位置,也就是為 NULL,指向 NULL 的指針通常被稱為空指針。

在鏈表中,數據一般都是分散存儲於記憶體中的,無須存儲在連續空間內。

因為數據都是分散存儲的,所以如果想要訪問數據,只能從第 1 個數據開始,順著指針的指向一一往下訪問(這便是順序訪問)。比如,想要找到 Red 這一數據,就得從 Blue 開始訪問,這之後,還要經過 Yellow,我們才能找到 Red。

如果想要添加數據,只需要改變添加位置前後的指針指向就可以,非常簡單。比如,在 Blue 和 Yellow 之間添加 Green。

首先將 Blue 的指針指向的位置變成 Green,然後再把 Green 的指針指向 Yellow,數據的添加就大功告成了。

數據的刪除也一樣,只要改變指針的指向就可以,比如刪除 Yellow。

這時,只需要把 Green 指針指向的位置從 Yellow 變成 Red,刪除就完成了。雖然 Yellow 本身還存儲在記憶體中,但是不管從哪裡都無法訪問這個數據,所以也就沒有特意去刪除它的必要了。今後需要用到 Yellow 所在的存儲空間時,只要用新數據覆蓋掉就可以了。

那麼對鏈表的操作所需的運行時間到底是多少呢?在這裡,我們把鏈表中的數據量記成 n。訪問數據時,我們需要從鏈表頭部開始查找(線性查找),如果目標數據在鏈表最後的話,需要的時間就是 O(n)。

另外,添加數據只需要更改兩個指針的指向,所以耗費的時間與 n 無關。如果已經到達了添加數據的位置,那麼添加操作只需花費 O(1)的時間,刪除數據同樣也只需 O(1)的時間。

鏈表的實現

在對鏈表有了大概的認識以後,我們用 Java 去實現屬於自己的鏈表:

public class ListNode {
    int val;
    ListNode next;

    ListNode(int x) {
        val = x;
    }
}

class MyLinkedList {
    int size;
    /**
     * 哨兵節點作為偽頭
     */
    ListNode head;

    public MyLinkedList() {
        size = 0;
        head = new ListNode(0);
    }

    /**
     * 獲取鏈表第 index 個節點的值。如果索引是無效的,返回-1
     *
     * @param index
     * @return
     */
    public int get(int index) {
        // 若索引無效
        if (index < 0 || index >= size) {
            return -1;
        }

        ListNode curr = head;
        // 從偽頭節點開始,向前走 index+1 步
        for (int i = 0; i < index + 1; ++i) {
            curr = curr.next;
        }
        return curr.val;
    }

    /**
     * 在頭部插入節點
     *
     * @param val
     */
    public void addAtHead(int val) {
        addAtIndex(0, val);
    }

    /**
     * 在尾部插入節點
     *
     * @param val
     */
    public void addAtTail(int val) {
        addAtIndex(size, val);
    }

    /**
     * 在鏈表中的第 index 個節點前添加值為 val 的一個節點。如果 index 等於鏈表的長度時,節點將被添加到鏈表的末尾。如果 index 大於鏈表長度,節點將無法插入
     *
     * @param index
     * @param val
     */
    public void addAtIndex(int index, int val) {
        // 如果index大於長度,則不會插入該節點。
        if (index > size) {
            return;
        }

        // 如果 index 為負,則該節點將插入列表的開頭。
        if (index < 0) {
            index = 0;
        }

        ++size;
        // 查找要添加的節點的前驅節點
        ListNode pred = head;
        for (int i = 0; i < index; ++i) {
            pred = pred.next;
        }

        // 要添加的節點
        ListNode toAdd = new ListNode(val);
        // 通過改變 next 來插入節點
        toAdd.next = pred.next;
        pred.next = toAdd;
    }

    /**
     * 如果 index 是有效的,刪除鏈表中的第 index 個節點
     *
     * @param index
     */
    public void deleteAtIndex(int index) {
        // 如果 index 無效,則不執行任何操作
        if (index < 0 || index >= size) {
            return;
        }

        size--;
        // 找到要刪除節點的前驅節點
        ListNode pred = head;
        for (int i = 0; i < index; ++i) {
            pred = pred.next;
        }

        // 通過改變 next 來刪除節點
        pred.next = pred.next.next;
    }
}

到這裡,我相信大家應該對鏈表有了進一步的理解,大家可以用不同的語言去設計實現下。

鏈表擴展

以上講述的鏈表是最基本的一種鏈表,除此之外,還存在幾種擴展方便的鏈表。

雖然上文中提到的鏈表在尾部沒有指針,但我們也可以在鏈表尾部使用指針,並且讓它指向鏈表頭部的數據,將鏈表變成環形,這便是迴圈鏈表,也叫環形鏈表。迴圈鏈表沒有頭和尾的概念,想要保存數量固定的最新數據時通常會使用這種鏈表。

迴圈鏈表

另外,以上提到的鏈表裡的每個數據都只有一個指針,但我們可以把指針設定為兩個,並且讓它們分別指向前後數據,這就是雙向鏈表。使用這種鏈表,不僅可以從前往後,還可以從後往前遍曆數據,十分方便。

但是,雙向鏈表存在兩個缺點:一是指針數的增加會導致存儲空間需求增加;二是添加和刪除數據時需要改變更多指針的指向。

雙向鏈表

總結

看完之後,相信大家都對鏈表和鏈表的基本操作有了一定的瞭解,還對迴圈鏈表和雙向鏈表有了初步的認識,大家可以使用自己喜歡的語言去設計實現下單向鏈表,有能力的話可以把迴圈鏈表和雙向鏈表也實現下。

說完鏈表,當然不能忘記經常和鏈表同時出現在面試官口中的—數組,將在接下來的文章對其進行展開介紹。

參考

《我的第一本演算法書》


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Kotlin Android項目可用的靜態檢查工具: Android官方的Lint, 第三方的ktlint和detekt. ...
  • SublimeText3 Emmet安裝問題網上已經很多帖子了,這個簡單,主要對win7 64位我本人遇到的Emmet好多快捷功能無法用(比如ul>li*4 Tab無法生成)問題做了整理!搜了好多文章最終找到問題所在! 希望能幫到大家,也給自己做個記錄! 軟體下載: 這篇文章提供的是Windows ...
  • 時間限制:C/C++ 1秒,其他語言2秒 空間限制:C/C++ 256M,其他語言512M 小易給你一個包含n個數字的數組。你可以對這個數組執行任意次以下交換操作: 對於數組中的兩個下標i,j(1<=i,j<=n),如果為奇數,就可以交換和。 現在允許你使用操作次數不限,小易希望你能求出在所有能通過 ...
  • 作為開發人員,我喜歡在編碼時聽音樂。管弦樂使我可以更加專註於自己的工作。有一天,我註意到我的手指隨著音樂節奏在鍵盤上跳舞。喜歡彈鋼琴。代碼中的每個單詞或符號都和諧地書寫。然後我想...聽起來如何...我每天編寫的代碼? 這個想法誕生了。 ⭐️ 繼續在soundcode.now.sh上 直播,放置您的 ...
  • 事件的移除 removeEventListener() 第二個參數需要指定要移除的事件句柄,不能是匿名函數,因為無法識別 想要移除成功,那麼三個參數必須跟addEventListener中的三個完全一致 <!DOCTYPE html> <html lang="en"> <head> <meta ch ...
  • 前言 基於 vuex 3.1.2 按如下流程進行分析: Vue.use(Vuex) Vue.use() 會執行插件的 install 方法,並把插件放入緩存數組中。 而 Vuex 的 install 方法很簡單,保證只執行一次,以及使用 applyMixin 初始化。 applyMixin 方法定義 ...
  • input文本框中設置提示信息,可以使用placeholder屬性,結果就是這樣,有當文本框內容為空時就顯示提示信息,當輸入內容時提示內容就消失了 ...
  • 本篇文章主要來介紹什麼是數據結構。 首先讓我們來看一張圖片: 數據存儲於電腦的記憶體中。記憶體如上圖所示,形似排成 1 列的箱子,1 個箱子里存儲 1 個數據。 數據存儲於記憶體時, 決定了數據順序和位置關係的便是數據結構 。 其實在我們生活中用到很多數據結構的知識,那麼舉一個我們生活中的慄子: 首先舉 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...