LeetCode算法題 (設計鏈表)Day16!!!C/C++

https://leetcode.cn/problems/design-linked-list/description/

一、題目分析

????????

你可以選擇使用單鏈表或者雙鏈表,設計并實現自己的鏈表。

單鏈表中的節點應該具備兩個屬性:val?和?next?。val?是當前節點的值,next?是指向下一個節點的指針/引用。

如果是雙向鏈表,則還需要屬性?prev?以指示鏈表中的上一個節點。假設鏈表中的所有節點下標從?0?開始。

實現?MyLinkedList?類:

  • MyLinkedList()?初始化?MyLinkedList?對象。
  • int get(int index)?獲取鏈表中下標為?index?的節點的值。如果下標無效,則返回?-1?。
  • void addAtHead(int val)?將一個值為?val?的節點插入到鏈表中第一個元素之前。在插入完成后,新節點會成為鏈表的第一個節點。
  • void addAtTail(int val)?將一個值為?val?的節點追加到鏈表中作為鏈表的最后一個元素。
  • void addAtIndex(int index, int val)?將一個值為?val?的節點插入到鏈表中下標為?index?的節點之前。如果?index?等于鏈表的長度,那么該節點會被追加到鏈表的末尾。如果?index?比長度更大,該節點將?不會插入?到鏈表中。
  • void deleteAtIndex(int index)?如果下標有效,則刪除鏈表中下標為?index?的節點。

示例:

輸入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
輸出
[null, null, null, null, 2, null, 3]解釋
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2);    // 鏈表變為 1->2->3
myLinkedList.get(1);              // 返回 2
myLinkedList.deleteAtIndex(1);    // 現在,鏈表變為 1->3
myLinkedList.get(1);              // 返回 3

? ? ? ? 今天的這道題目算得上是目前碰到的最多字數的題了,但是需求很簡單,實現出四個功能即可。分別為:

  1. int get(int index)獲取鏈表中下標為index的節點的值
  2. void addAtHead(int val)將一個val的節點插入到鏈表中第一個元素之前,也就是頭插操作。
  3. void addAtTail(int val)將一個值為val的節點追加到鏈表中作為鏈表的最后一個元素,也就是尾插操作。
  4. void addAtIndex(int index, int val)將一個值為val的節點插入到鏈表中下標為index的節點之前,這里對應隨機插入操作。
  5. void deleteAtIndex(int index)?如果下標有效,則刪除鏈表中下標為?index?的節點。

二、示例分析

輸入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
輸出
[null, null, null, null, 2, null, 3]解釋
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2);    // 鏈表變為 1->2->3
myLinkedList.get(1);              // 返回 2
myLinkedList.deleteAtIndex(1);    // 現在,鏈表變為 1->3
myLinkedList.get(1);              // 返回 3

三、設計思路&代碼實現

? ? ? ? 首先題目說明可以使用單鏈表||雙鏈表,這里我們選用單鏈表的解法,只要掌握了單鏈表的一些基本操作,再使用雙鏈表進行實現起來就會很容易。

?一、創建結構體

????????這里我們需要創建一個結構體,之前在和大家分享鏈表的中間節點那里,有提到過鏈表這種數據結構的存儲方式,如果忘記的同學,可以點擊鏈接重新回憶一下。

https://blog.csdn.net/m0_75144071/article/details/144828160?spm=1001.2014.3001.5502

// 定義鏈表節點的結構體
struct LinkNode {int val;          // 節點存儲的數據值LinkNode* next;   // 指向下一個節點的指針// 構造函數,用于初始化新節點// 參數 val: 要存儲在節點中的值LinkNode(int val) : val(val),     // 初始化 val 成員為傳入的值next(nullptr)  // 初始化 next 指針為 nullptr(表示這是尾節點){// 構造函數體為空,因為初始化列表已經完成了所有初始化工作}
};

?????????LinkNode是一個表示節點的結構體,包含兩個成員,分別為val用于存儲節點的數據(本題用到的數據類型為int型),next用于指向一下個節點的指針(LinkNode*類型)

二、初始化鏈表

? ? ? ? 在昨天的題目中給大家介紹了使用虛擬頭節點進行一些鏈表的基本操作時會很方便,今天我們同樣采用虛擬帶有虛擬頭節點的方式來實現。忘記的同學,可以點擊鏈接再重新回顧一下。

https://blog.csdn.net/m0_75144071/article/details/147662796?spm=1001.2014.3001.5502

// MyLinkedList 類的構造函數
MyLinkedList() {// 創建一個虛擬頭節點(dummy node),其值初始化為0// 虛擬頭節點不存儲實際數據,它的存在是為了簡化鏈表操作// 例如在鏈表頭部插入/刪除節點時不需要特殊處理dummyHead = new LinkNode(0);// 初始化鏈表大小為0,表示當前鏈表為空(只有虛擬頭節點)size = 0;
}

三、查找功能(按位序查找)

? ? ? ? 鏈表這種數據結構有一種缺點,不支持隨機訪問,也就是他沒有辦法做到像數組那樣,我只要有了元素的下標就可以用常數級的時間復雜度來找到這個元素。使用鏈表查找元素時復雜度為O(n)(最壞)。所以我們需要從頭開始遍歷才可以進行查找。

/*** 獲取鏈表中指定位置的元素值* index 要獲取的元素位置索引(從0開始)* return 如果索引有效則返回對應節點的值,否則返回-1*/
int get(int index) {// 邊界檢查:如果索引超出有效范圍(小于0或大于等于鏈表長度)// 則返回-1表示無效if (index > (size - 1) || index < 0)return -1;// 初始化當前指針指向第一個實際節點(跳過虛擬頭節點)LinkNode* cur = dummyHead->next;// 遍歷鏈表直到目標位置// 使用index--的方式移動指針,循環次數即為需要移動的步數while (index--) {cur = cur->next;}// 返回找到的節點的值return cur->val;
}

四、頭插操作

? ? ? ? 在進行頭插入/頭刪除操作時候,鏈表的效率就會比順序表(數組)高很多,?鏈表的復雜度為O(1),而數組則需要O(n)。

????????

/*** 在鏈表頭部插入一個新節點* val 要插入的節點值*/
void addAtHead(int val) {LinkNode* newNode = new LinkNode(val);// 新節點指向當前第一個節點newNode->next = dummyHead->next;// 更新dummyHead指向新節點dummyHead->next = newNode;// 增加鏈表長度size++;
}

以下為圖文講解:?

五、尾插操作

? ? ? ? 鏈表的尾插操作由于每次需要從頭節點開始遍歷找到最后的尾部節點,所以整體時間復雜度為O(n)。

void addAtTail(int val) {LinkNode* newNode = new LinkNode(val); newNode->next = nullptr;  // 由于是單鏈表的尾部插入,所以next的的指向應為nullptrLinkNode* cur = dummyHead;while (cur->next) {  // 遍歷直到找到最后一個節點cur = cur->next;}cur->next = newNode;  // 將新節點接在尾部size++; // 增加鏈表大小
}

圖文講解:?

?

六、按位序的插入操作

? ? ? ? 在進行按位序插入操作時,鏈表整體的時間復雜度以查找位置為主導為O(n)?(最壞情況下),相比數組來說,時間復雜度整體相同。

/*** 在鏈表的指定位置插入一個新節點* index 要插入的位置索引(從0開始)* val 要插入的節點值*/
void addAtIndex(int index, int val) {// 1. 邊界檢查:如果索引大于當前鏈表長度或索引小于0,直接返回不執行插入if (index > size || index < 0)return;// 2. 創建新節點,使用構造函數直接初始化節點值LinkNode* newNode = new LinkNode(val);// 3. 定位到要插入位置的前驅節點LinkNode* cur = dummyHead;  // 從虛擬頭節點開始while (index--) {           // 循環index次,移動到插入位置的前一個節點cur = cur->next;}// 4. 執行插入操作newNode->next = cur->next;  // 新節點的next指向原位置的節點cur->next = newNode;        // 前驅節點的next指向新節點// 5. 更新鏈表長度size++;
}

圖文解釋:

七、按位序的刪除操作?

? ? ? ??在鏈表中的按位序刪除與插入相同。鏈表整體的時間復雜度以查找位置為主導為O(n)?(最壞情況下),相比數組來說,時間復雜度整體相同。

// 刪除指定索引位置的節點
// 參數:
// index: 要刪除節點的索引,索引從 0 開始
void deleteAtIndex(int index) {// 檢查索引是否越界,如果索引大于等于鏈表的大小或者小于 0,直接返回if (index >= size || index < 0)return;// 定義一個指針 cur,初始指向虛擬頭節點 dummyHead// 虛擬頭節點的作用是簡化鏈表頭節點的操作LinkNode* cur = dummyHead;// 通過循環讓 cur 指針移動到要刪除節點的前一個節點// 循環條件 index-- 表示每次循環后 index 的值減 1,直到 index 變為 0while (index--) {cur = cur->next;}// 定義一個臨時指針 temp,指向要刪除的節點// 即 cur 指針當前所指節點的下一個節點LinkNode* temp = cur->next;// 調整 cur 指針的 next 指針,使其跳過要刪除的節點// 直接指向要刪除節點的下一個節點cur->next = cur->next->next;// 釋放要刪除節點所占用的內存,防止內存泄漏delete temp;// 鏈表的大小減 1,因為成功刪除了一個節點size--;
}

圖文講解:

至此本題需實現的所有功能均已實現完畢!完結撒花!!!🌸🌸🌸

八、完整代碼

????????C++:

class MyLinkedList {
public:struct LinkNode {int val;LinkNode* next;LinkNode(int val) : val(val), next(nullptr) {}};MyLinkedList() {dummyHead = new LinkNode(0);size = 0;}int get(int index) {if (index > (size - 1) || index < 0)return -1;LinkNode* cur = dummyHead->next;while (index--) {cur = cur->next;}return cur->val;}void addAtHead(int val) {LinkNode* newNode = new LinkNode(0);newNode->val = val;newNode->next = dummyHead->next;dummyHead->next = newNode;size++;}void addAtTail(int val) {LinkNode* newNode = new LinkNode(0);newNode->val = val;newNode->next = NULL;LinkNode* cur = dummyHead;while (cur->next) {cur = cur->next;}cur->next = newNode;size++;}void addAtIndex(int index, int val) {if (index > size)return;LinkNode* newNode = new LinkNode(val);LinkNode* cur = dummyHead;while (index--) {cur = cur->next;}newNode->next = cur->next;cur->next = newNode;size++;}void deleteAtIndex(int index) {if (index >= size || index < 0)return;LinkNode* cur = dummyHead;while (index--) {cur = cur->next;}LinkNode* temp = cur->next;cur->next = cur->next->next;delete temp;size--;}void Print() {LinkNode* cur = dummyHead->next;while (cur->next) {cout << cur->val << " ";cur = cur->next;}cout << endl;}private:int size;LinkNode* dummyHead;
};/*** Your MyLinkedList object will be instantiated and called as such:* MyLinkedList* obj = new MyLinkedList();* int param_1 = obj->get(index);* obj->addAtHead(val);* obj->addAtTail(val);* obj->addAtIndex(index,val);* obj->deleteAtIndex(index);*/

四、鏈表與數組的對比

鏈表 vs 數組 操作對比總結

時間復雜度對比
操作鏈表數組勝出方
隨機訪問O(n)O(1)數組
頭部插入/刪除O(1)O(n)鏈表
尾部插入/刪除O(n)O(1)數組
中間插入/刪除O(n)O(n)平手
核心特點
  • 鏈表:動態內存、插入刪除快(尤其頭部)、訪問慢

  • 數組:連續內存、訪問快、插入刪除慢(需移動元素)

適用場景
  • 鏈表:頻繁在頭部/中部增刪(如棧、隊列)

  • 數組:頻繁隨機訪問或尾部操作(如數值計算)

五、題目總結?

????????這道題目讓我們自己動手實現一個鏈表數據結構,主要包含獲取節點值、頭部插入、尾部插入、指定位置插入和刪除這五個核心操作。通過這個實現過程,我們深入理解了鏈表的基本特性和操作原理。

????????鏈表最大的特點是插入刪除高效,尤其是頭部操作只需要O(1)時間,這比數組要快很多。但是鏈表訪問元素需要從頭遍歷,時間復雜度是O(n),不如數組的隨機訪問快。在實際開發中,如果經常需要在頭部插入刪除數據,鏈表是更好的選擇;如果需要頻繁按位置訪問數據,數組會更合適。

????????實現鏈表時要注意幾個關鍵點:使用虛擬頭節點可以簡化操作;維護鏈表長度size變量能方便邊界檢查;指針操作要特別注意順序,避免出現斷鏈的情況。這些細節處理能力是成為一名合格程序員的基本功。

????????通過這道題目,我們不僅掌握了鏈表的實現方法,更重要的是理解了不同數據結構的適用場景。在實際工程中,要根據具體需求選擇最合適的數據結構,這是寫出高效代碼的重要基礎。鏈表作為基礎數據結構,它的思想在很多高級數據結構中都有體現,學好鏈表對后續學習樹、圖等結構很有幫助。今天的分享到此結束!謝謝大家!!!荊軻刺秦!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/79508.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/79508.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/79508.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

《解鎖GCC版本升級:開啟編程新世界大門》

《解鎖GCC版本升級:開啟編程新世界大門》 一、引言:GCC 版本升級的魔法鑰匙 在編程的廣闊天地里,GCC(GNU Compiler Collection)宛如一座燈塔,為無數開發者照亮前行的道路。它是一款開源且功能強大的編譯器集合,支持 C、C++、Objective - C、Fortran、Ada 等多種編程語言…

toLua筆記

基本 LuaState luaStatenew LuaState(); luaState.Start(); luaState.DoString("xxx"); luaState.DoFile("yyy.lua"); luaState.Require("zzz");//不要加.lua后綴 luaState.CheckTop();//檢查解析器棧頂為空 luaState.Dispose(); luaStatenull;…

go實現雙向鏈表

需求 實現雙向鏈表的節點生成、正反向遍歷、指定刪除。 實現 package mainimport ("fmt" )type zodiac_sign struct {number intdizhi stringanimal stringyear intprevious *zodiac_signnext *zodiac_sign }// 添加 // func add_node_by_order(pr…

AI實踐指南:AGENT、RAG和MCP在Java中的簡單實現

在當今AI快速發展的時代&#xff0c;有幾個核心概念正在改變我們構建智能應用的方式。本文將用簡單易懂的語言介紹三個重要概念&#xff1a;AGENT&#xff08;AI代理&#xff09;、RAG&#xff08;檢索增強生成&#xff09;和MCP&#xff08;多通道感知&#xff09;&#xff0c…

解決VMware虛擬機能搜索到網頁但打不開的問題

&#x1f334; 問題描述 很奇怪&#xff0c;不知道為什么&#xff0c;我安裝的Windows 10虛擬機能在瀏覽器中搜索到網頁&#xff0c;但點擊具體的網頁鏈接就是死活不能加載出來&#xff0c;如下圖所示&#xff1a; 點擊第一個鏈接&#xff0c;加載了四五分鐘&#xff0c;結果就…

JVM性能調優的基礎知識 | JVM內部優化與運行時優化

目錄 JVM內部的優化邏輯 JVM的執行引擎 解釋執行器 即時編譯器 JVM采用哪種方式&#xff1f; 即時編譯器類型 JVM的分層編譯5大級別&#xff1a; 分層編譯級別&#xff1a; 熱點代碼&#xff1a; 如何找到熱點代碼&#xff1f; java兩大計數器&#xff1a; OSR 編譯…

什么是多租戶系統

隨著云計算和 SaaS&#xff08;Software as a Service&#xff09;模式的普及&#xff0c;多租戶架構&#xff08;Multi-Tenant Architecture&#xff09;成為 SaaS 產品設計中的核心模式之一。多租戶架構允許多個用戶&#xff08;租戶&#xff09;共享同一套基礎設施和應用&am…

多線程系列三:這就是線程的狀態?

1.認識線程的狀態 NEW&#xff1a;Thread對象已經創建好了&#xff0c;但還沒有調用start方法在系統中創建線程 RUNNABLE&#xff1a;就緒狀態&#xff0c;表示這個線程正在CPU上執行&#xff0c;或準備就緒&#xff0c;隨時可以去CPU上執行 BLOCKED&#xff1a;表示由于鎖競爭…

【C語言練習】019. 使用結構體數組存儲復雜數據

019. 使用結構體數組存儲復雜數據 019. 使用結構體數組存儲復雜數據示例1&#xff1a;定義一個結構體并創建結構體數組定義結構體創建并初始化結構體數組輸出結果 示例2&#xff1a;動態輸入數據到結構體數組定義結構體動態輸入數據示例輸入和輸出 示例3&#xff1a;使用結構體…

**Java面試大冒險:謝飛機的幽默與技術碰撞記**

互聯網大廠Java求職者面試&#xff1a;一場嚴肅與搞笑交織的技術盛宴 場景&#xff1a; 互聯網大廠面試間 人物&#xff1a; 面試官&#xff1a; 一位嚴肅的資深架構師&#xff0c;對技術要求嚴格。謝飛機&#xff1a; 一位搞笑的程序員&#xff0c;技術實力參差不齊。 第一…

MySQL進階(三)

五、鎖 1. 概述 鎖是計算機協調多個進程或線程并發訪問某一資源的機制&#xff08;避免爭搶&#xff09;。 在數據庫中&#xff0c;除傳統的計算資源&#xff08;如 CPU、RAM、I/O 等&#xff09;的爭用以外&#xff0c;數據也是一種供許多用戶共享的資源。如何保證數據并發…

【BLE】【nRF Connect】 精講nRF Connect自動化測試套件(宏錄制、XML腳本)

目錄 前言 1. nRF Connect自動化測試介紹 1.1. nRF connect宏錄制功能介紹 1.2. 電腦端XML方式 1.3 實際應用案例 1.3.1 BLE 穩定性測試 1.3.2 設備固件更新(DFU)測試 1.3.3 批量設備配置 1.4 操作步驟 1.5 注意事項 2. nRF Connect日志記錄 2.1. 日志記錄功能 …

【數據結構】堆的完整實現

堆的完整實現 堆的完整實現GitHub地址前言堆的核心功能實現重溫堆的定義堆結構定義1. 堆初始化與銷毀2. 元素交換函數3. 堆化操作向上調整&#xff08;子→父&#xff09;向下調整&#xff08;父→子&#xff09; 4. 堆元素插入5. 堆元素刪除6. 輔助功能函數堆的判空獲取堆頂元…

如何優化MySQL主從復制的性能?

優化MySQL主從復制的性能需要從硬件、配置、架構設計和運維策略等多方面入手。以下是詳細的優化方案&#xff1a; 一、減少主庫寫入壓力 1. ?主庫優化? 二進制日志&#xff08;binlog&#xff09;優化?&#xff1a; 使用 binlog_formatROW 以獲得更高效的復制和更少的數…

MySQL安裝完全指南:從零開始到配置優化(附避坑指南)

&#x1f525; 前言&#xff1a;為什么你總是裝不好MySQL&#xff1f; &#xff08;實話實說&#xff09;每次看到新手在MySQL安裝環節瘋狂踩坑&#xff0c;老司機都忍不住想摔鍵盤&#xff01;明明官網下載的安裝包&#xff0c;怎么就會報錯呢&#xff1f;為什么別人的環境變…

密碼學_加密

目錄 密碼學 01 密碼基礎進制與計量 02 加解密基操 替換 移位 編碼 編碼 置換 移位 加解密強度 03 對稱加密算法(私鑰) 工作過程 缺陷 對稱加密算法列舉&#xff1f; DES DES算法架構 DES分組加密公式 DES中ECB-CBC兩種加密方式 3DES 由于DES密鑰太短&#xf…

輕量級RTSP服務模塊:跨平臺低延遲嵌入即用的流媒體引擎

在音視頻流媒體系統中&#xff0c;RTSP&#xff08;Real-Time Streaming Protocol&#xff09;服務模塊通常扮演著“視頻分發中心”的角色&#xff0c;它將編碼后的音視頻內容轉為標準的流媒體格式&#xff0c;供客戶端&#xff08;播放器、云端平臺、AI模塊等&#xff09;拉流…

Nginx發布Vue(ElementPlus),與.NETCore對接(騰訊云)

案例資料鏈接&#xff1a;https://download.csdn.net/download/ly1h1/90745660 1.邏輯說明 1.1 邏輯示意圖 # 前端請求處理邏輯圖瀏覽器請求流程: 1. 瀏覽器發起請求├─ 開發環境(DEV)│ ├─ 請求URL: http://192.168.0.102:3000/api/xxx│ └─ 被Vite代理處理└─ 生產…

解析機器人 2.0.2 | 支持超過50種短視頻平臺的鏈接解析,無水印提取,多功能下載工具

解析機器人是一款功能強大的工具軟件&#xff0c;登錄即可解鎖會員特權。它支持超過50種短視頻平臺的鏈接解析&#xff0c;包括抖音、快手、西瓜、bilibili等&#xff0c;并能實現無水印提取。此外&#xff0c;還提供P2P下載、磁力鏈等多種下載方式&#xff0c;確保用戶能夠快速…

C++ - 數據容器之 forward_list(創建與初始化、元素訪問、容量判斷、元素遍歷、添加元素、刪除元素)

一、創建與初始化 引入 <forward_list> 并使用 std 命名空間 #include <forward_list>using namespace std;創建一個空 forward_list forward_list<int> fl;創建一個包含 5 個元素&#xff0c;每個元素初始化為 0 的 forward_list forward_list<int&g…