探索鏈表的奇妙世界:從基礎到高級應用

鏈表是計算機科學中一種基礎且重要的數據結構,它如同一條由珠子串成的項鏈,每個珠子(節點)都包含著數據和指向下一個珠子的線索。

與數組相比,鏈表在插入和刪除操作上更加靈活,無需預先分配固定大小的內存空間。

一、單鏈表:數據結構的入門之選

基本形態與特點

單鏈表是鏈表家族中最基礎的成員,它由一系列節點組成,每個節點包含兩部分:

數據域和指針域

數據域存儲具體的數據,而指針域則指向下一個節點。

鏈表的起點由一個頭指針(head)指向,最后一個節點的指針域指向 NULL,表示鏈表的結束。

特點總結

????????單向訪問:只能從頭節點開始,沿著指針方向依次訪問后續節點

????????插入和刪除操作效率高:O (1) 時間復雜度(前提是已知操作位置的前驅節點)

????????動態內存分配:無需預先分配固定大小的內存

????????隨機訪問效率低:訪問第 n 個節點需要 O (n) 時間

示例代碼:單鏈表的實現

#include <stdio.h>
#include <stdlib.h>// 定義單鏈表節點結構
typedef struct Node {int data;           // 數據域struct Node* next;  // 指針域,指向下一個節點
} Node;// 創建新節點
Node* createNode(int data) {Node* newNode = (Node*)malloc(sizeof(Node));if (newNode == NULL) {printf("內存分配失敗\n");exit(1);}newNode->data = data;newNode->next = NULL;return newNode;
}// 在鏈表尾部插入節點
void append(Node** head_ref, int data) {Node* newNode = createNode(data);if (*head_ref == NULL) {*head_ref = newNode;return;}Node* last = *head_ref;while (last->next != NULL) {last = last->next;}last->next = newNode;
}// 打印鏈表
void printList(Node* node) {while (node != NULL) {printf("%d -> ", node->data);node = node->next;}printf("NULL\n");
}// 釋放鏈表內存
void freeList(Node* head) {Node* temp;while (head != NULL) {temp = head;head = head->next;free(temp);}
}int main() {Node* head = NULL;append(&head, 1);append(&head, 2);append(&head, 3);printf("單鏈表內容: ");printList(head);freeList(head);return 0;
}

單鏈表示意圖

頭指針 head↓┌───────┐    ┌───────┐    ┌───────┐│ 1 | ──┼──→ │ 2 | ──┼──→ │ 3 | NULL└───────┘    └───────┘    └───────┘

二、雙向鏈表:支持雙向導航的靈活結構

基本形態與特點

雙向鏈表在單鏈表的基礎上進行了擴展,每個節點除了包含數據域和指向下一個節點的指針外,還增加了一個指向前驅節點的指針。

這種結構使得雙向鏈表支持雙向遍歷,既可以從頭節點向后訪問,也可以從尾節點向前訪問。

特點總結

????????????????雙向訪問:支持從頭節點和尾節點兩個方向進行遍歷

????????????????插入和刪除操作更加靈活:可以快速訪問前驅節點

????????????????每個節點占用更多內存:需要額外的指針域存儲前驅節點信息

????????????????操作復雜度增加:插入和刪除操作需要維護兩個指針

示例代碼:雙向鏈表的實現

#include <stdio.h>
#include <stdlib.h>// 定義雙向鏈表節點結構
typedef struct DNode {int data;               // 數據域struct DNode* prev;     // 指向前驅節點的指針struct DNode* next;     // 指向后繼節點的指針
} DNode;// 創建新節點
DNode* createDNode(int data) {DNode* newNode = (DNode*)malloc(sizeof(DNode));if (newNode == NULL) {printf("內存分配失敗\n");exit(1);}newNode->data = data;newNode->prev = NULL;newNode->next = NULL;return newNode;
}// 在鏈表尾部插入節點
void appendDNode(DNode** head_ref, int data) {DNode* newNode = createDNode(data);if (*head_ref == NULL) {*head_ref = newNode;return;}DNode* last = *head_ref;while (last->next != NULL) {last = last->next;}last->next = newNode;newNode->prev = last;
}// 打印雙向鏈表(正向)
void printForward(DNode* node) {while (node != NULL) {printf("%d <-> ", node->data);node = node->next;}printf("NULL\n");
}// 釋放雙向鏈表內存
void freeDList(DNode* head) {DNode* temp;while (head != NULL) {temp = head;head = head->next;free(temp);}
}int main() {DNode* head = NULL;appendDNode(&head, 1);appendDNode(&head, 2);appendDNode(&head, 3);printf("雙向鏈表內容(正向): ");printForward(head);freeDList(head);return 0;
}

雙向鏈表示意圖

頭指針 head↓┌───────┐    ┌───────┐    ┌───────┐
NULL←─| 1 | ──┼──→| 2 | ──┼──→| 3 |─→NULL└───────┘    └───────┘    └───────┘

三、循環鏈表:首尾相連的封閉結構

基本形態與特點

循環鏈表是一種特殊的鏈表結構,它的最后一個節點的指針域不再指向 NULL,而是指向鏈表的頭節點,形成一個閉合的環。

循環鏈表可以是單循環鏈表(只使用一個方向的指針)或雙循環鏈表(使用雙向指針)。

特點總結

????????????????首尾相連:可以從任意節點開始遍歷整個鏈表

????????????????沒有明顯的頭節點和尾節點:需要特別注意循環終止條件

? ? ? ? ? ? ? ? 適合實現環形緩沖區、輪詢調度等場景

????????????????插入和刪除操作需要考慮維護循環特性

示例代碼:單循環鏈表的實現

#include <stdio.h>
#include <stdlib.h>// 定義循環鏈表節點結構
typedef struct CNode {int data;struct CNode* next;
} CNode;// 創建新節點
CNode* createCNode(int data) {CNode* newNode = (CNode*)malloc(sizeof(CNode));if (newNode == NULL) {printf("內存分配失敗\n");exit(1);}newNode->data = data;newNode->next = newNode;  // 初始時指向自身return newNode;
}// 在鏈表尾部插入節點
void appendCNode(CNode** head_ref, int data) {CNode* newNode = createCNode(data);if (*head_ref == NULL) {*head_ref = newNode;return;}CNode* last = (*head_ref)->next;  // 找到尾節點while (last->next != *head_ref) {last = last->next;}last->next = newNode;newNode->next = *head_ref;
}// 打印循環鏈表
void printCList(CNode* head) {if (head == NULL) return;CNode* temp = head;do {printf("%d -> ", temp->data);temp = temp->next;} while (temp != head);printf("(回到頭節點)\n");
}// 釋放循環鏈表內存
void freeCList(CNode* head) {if (head == NULL) return;CNode* temp = head->next;while (temp != head) {CNode* next = temp->next;free(temp);temp = next;}free(head);
}int main() {CNode* head = NULL;appendCNode(&head, 1);appendCNode(&head, 2);appendCNode(&head, 3);printf("循環鏈表內容: ");printCList(head);freeCList(head);return 0;
}

單循環鏈表示意圖

   ┌───────┐    ┌───────┐    ┌───────┐│ 1 | ──┼──→ │ 2 | ──┼──→ │ 3 | ──┼──┐└───────┘    └───────┘    └───────┘  │↑                                  │└──────────────────────────────────┘

四、雙向循環鏈表:最靈活的鏈表結構

基本形態與特點

雙向循環鏈表結合了雙向鏈表和循環鏈表的特點,每個節點既包含指向前驅節點的指針,也包含指向后繼節點的指針,并且鏈表的首尾節點相連形成一個環。

這種結構提供了最大的靈活性,但也需要更多的內存和更復雜的操作。

特點總結

雙向遍歷:可以從任意節點開始向前或向后遍歷首尾相連:

沒有明顯的頭節點和尾節點插入和刪除操作

需要維護四個指針(前驅和后繼節點的指針)

適合需要頻繁雙向遍歷和循環操作的場景

示例代碼:雙向循環鏈表的實現

#include <stdio.h>
#include <stdlib.h>// 定義雙向循環鏈表節點結構
typedef struct DCNode {int data;struct DCNode* prev;struct DCNode* next;
} DCNode;// 創建新節點
DCNode* createDCNode(int data) {DCNode* newNode = (DCNode*)malloc(sizeof(DCNode));if (newNode == NULL) {printf("內存分配失敗\n");exit(1);}newNode->data = data;newNode->prev = newNode;newNode->next = newNode;return newNode;
}// 在鏈表尾部插入節點
void appendDCNode(DCNode** head_ref, int data) {DCNode* newNode = createDCNode(data);if (*head_ref == NULL) {*head_ref = newNode;return;}DCNode* last = (*head_ref)->prev;  // 找到尾節點last->next = newNode;newNode->prev = last;newNode->next = *head_ref;(*head_ref)->prev = newNode;
}// 打印雙向循環鏈表(正向)
void printDCListForward(DCNode* head) {if (head == NULL) return;DCNode* temp = head;do {printf("%d <-> ", temp->data);temp = temp->next;} while (temp != head);printf("(回到頭節點)\n");
}// 釋放雙向循環鏈表內存
void freeDCList(DCNode* head) {if (head == NULL) return;DCNode* temp = head->next;while (temp != head) {DCNode* next = temp->next;free(temp);temp = next;}free(head);
}int main() {DCNode* head = NULL;appendDCNode(&head, 1);appendDCNode(&head, 2);appendDCNode(&head, 3);printf("雙向循環鏈表內容(正向): ");printDCListForward(head);freeDCList(head);return 0;
}

雙向循環鏈表示意圖

   ┌───────┐    ┌───────┐    ┌───────┐│ 1 | ──┼──→ │ 2 | ──┼──→ │ 3 | ──┼──┐└───────┘    └───────┘    └───────┘  │↑  ↑                                ││  └────────────────────────────────┘│                                     │└─────────────────────────────────────┘

五、鏈表與數組的對比

特性鏈表數組
內存分配動態分配,無需預先指定大小靜態分配,需要預先指定大小
插入 / 刪除效率O (1)(已知位置)O (n)(需要移動元素)
隨機訪問效率O(n)O(1)
內存利用率可能有碎片(每個節點需要額外指針)連續內存,無碎片
適用場景頻繁插入刪除,數據量不確定頻繁隨機訪問,數據量固定

總結

鏈表作為一種基礎的數據結構,在計算機科學中有著廣泛的應用。通過本文的介紹,我們了解了鏈表的四種主要類型:

????????單鏈表:最簡單的鏈表形式,支持單向遍歷

????????雙向鏈表:增加了前驅指針,支持雙向遍歷

????????循環鏈表:首尾相連,適合環形結構的應用

????????雙向循環鏈表:結合了雙向鏈表和循環鏈表的特點,提供最大的靈活性

每種鏈表結構都有其獨特的形態、特點和適用場景。

在實際編程中,我們可以根據具體需求選擇合適的鏈表類型。

鏈表的核心優勢在于動態內存分配和高效的插入刪除操作,這使得它在許多場景下比數組更加適用。

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

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

相關文章

黑馬點評雙攔截器和Threadlocal實現原理

文章目錄 雙攔截器ThreadLocal實現原理 雙攔截器 實現登錄狀態刷新的原因&#xff1a; ? 防止用戶會話過期&#xff1a;通過動態刷新Token有效期&#xff0c;確保活躍用戶不會因固定過期時間而被強制登出 ? 提升用戶體驗&#xff1a;用戶無需頻繁重新登錄&#xff0c;只要…

Windows 中動態庫.dll 的 .lib 文件有什么作用?

在 Windows 平臺開發中, 動態鏈接庫(Dynamic Link Library, DLL)。與之相關的還有一個常讓人困惑的文件——.lib 文件。那么,這個 .lib 文件到底有什么作用呢? 一、什么是 .lib 文件? .lib 文件是 靜態導入庫(Import Library) 文件,它通常與動態鏈接庫(DLL)一起生成…

細說STM32單片機FreeRTOS消息緩沖區及其應用實例

目錄 一、消息緩沖區功能概述 二、消息緩沖區操作相關函數 1、相關函數概述 2、部分函數詳解 &#xff08;1&#xff09;創建消息緩沖區 &#xff08;2&#xff09;寫入消息 &#xff08;3&#xff09;讀取消息 &#xff08;4&#xff09;消息緩沖區狀態查詢 三、消息…

【緩存】JAVA本地緩存推薦Caffeine和Guava

&#x1f31f; 引言 在軟件開發過程中&#xff0c;緩存是提升系統性能的常用手段。對于基礎場景&#xff0c;直接使用 Java集合框架&#xff08;如Map/Set/List&#xff09;即可滿足需求。然而&#xff0c;當面對更復雜的緩存場景時&#xff1a; 需要支持多種過期策略&#x…

IDA插件 MIPSROP的安裝和使用方法

前言 筆者的IDA版本為9.0&#xff0c;剛開始根據一些博客描述以為將mipsrop.py拷貝到IDA的plugins目錄即可&#xff0c;可操作后發現事情好像沒這么簡單&#xff0c;復制進去后就發現沒有博客中所說的 MIPS ROP Finder &#xff0c;筆者在網上搜索了很多博客后在 https://bbs.…

(1)轉置后,行列式的值不變 (2)將行列式的任意兩行互換位置后,行列式改變符號

以下是對原始內容在不改變內容本身的前提下進行的格式優化&#xff0c;以提升可讀性和邏輯清晰度&#xff1a; ? 行列式的幾何意義 行列式&#xff08;determinant&#xff09;是線性代數中一個非常重要的概念&#xff0c;它的幾何含義可以從以下幾個方面理解&#xff1a; &a…

最大似然估計(Maximum Likelihood Estimation, MLE)詳解

一、定義 最大似然估計 是一種參數估計方法&#xff0c;其核心思想是&#xff1a; 選擇能使觀測數據出現概率最大的參數值作為估計值。 具體來說&#xff0c;假設數據 D x 1 , x 2 , … , x n D{x_1,x_2,…,x_n} Dx1?,x2?,…,xn?獨立且服從某個概率分布 P ( x ∣ θ ) P(…

用go從零構建寫一個RPC(3)--異步調用+多路復用實現

在前兩個版本中&#xff0c;我們實現了基礎的客戶端-服務端通信、連接池、序列化等關鍵模塊。為了進一步提升吞吐量和并發性能&#xff0c;本版本新增了 異步發送機制 和 多路復用支持&#xff0c;旨在減少資源消耗、提升連接利用率。 代碼地址&#xff1a;https://github.com/…

FFmpeg 安裝包全攻略:gpl、lgpl、shared、master 區別詳解

這些 FFmpeg 安裝包有很多版本和變種&#xff0c;主要區別在于以下幾個方面&#xff1a; ? 一、從名稱中看出的關鍵參數&#xff1a; 1. 版本號 master&#xff1a;開發版&#xff0c;最新功能&#xff0c;但可能不穩定。n6.1 / n7.1&#xff1a;正式版本&#xff0c;更穩定…

深度學習實戰:從圖像分類到文本生成的完整案例解析

1 圖像分類案例 1.1 CIFAR10數據集介紹 cifar數據是torchvision第三方包提供的數據集 訓練集5w 測試集1w y標簽 10個類別 10分類問題 一張圖形狀 (32, 32, 3) import torch import torch.nn as nn from torchvision.datasets import CIFAR10 from torchvision.transforms i…

Android 添加系統服務的完整流程

[應用程序] (應用進程)│↓ 調用簡單API [SoundManager] │ ├─ 代理模式門面模式&#xff08;應用進程&#xff09;│ ├─ 緩存數據 ←─ 裝飾器模式&#xff08;應用進程&#xff09;│ └─ 轉換異常 ←─ 適配器模式&#xff08;應用進程&#xff09;│↓ 通過Bind…

wan2.1代碼筆記

GPU內存不夠&#xff0c;可以先運行umt5&#xff0c;然后再運行wanpipeline&#xff0c;參考FLUX.1代碼筆記&#xff0c;或者使用ComfyUI。 下面使用隨機數代替umt5 embedding。 import torch from diffusers.utils import export_to_video from diffusers import Autoencoder…

環境搭建與工具配置

3.1 本地環境搭建 3.1.1 WAMP環境搭建漏洞靶場&#xff08;一、二&#xff09; WAMP&#xff08;Windows Apache MySQL PHP&#xff09;是搭建本地Web漏洞靶場的基礎環境。 安裝步驟&#xff1a; Apache&#xff1a;下載并安裝最新版Apache HTTP Server&#xff0c;配置監…

STM32F446主時鐘失效時DAC輸出異常現象解析與解決方案

—### 現象概述 在STM32F446微控制器應用中&#xff0c;若主時鐘&#xff08;HSE&#xff09;的晶體信號對地短路&#xff0c;但DAC&#xff08;數模轉換器&#xff09;仍能輸出變化信號&#xff0c;這一現象看似矛盾&#xff0c;實則與系統時鐘切換機制密切相關。本文將從硬件…

React 如何封裝一個可復用的 Ant Design 組件

文章目錄 前言一、為什么需要封裝組件&#xff1f;二、 仿antd組件的Button按鈕三、封裝一個可復用的表格組件 (實戰)1. 明確需求2. 設計組件 API3. 實現組件代碼4. 使用組件 三、封裝組件的最佳實踐四、進階優化 總結 前言 作為一名前端開發工程師&#xff0c;在日常項目中&a…

STC89C52RC/LE52RC

STC89C52RC 芯片手冊原理圖擴展版原理圖 功能示例LED燈LED燈的常亮效果LED燈的閃爍LED燈的跑馬燈效果&#xff1a;從左到右&#xff0c;從右到左 數碼管靜態數碼管數碼管計數mian.cApp.cApp.hCom.cCom.hDir.cDir.hInt.cInt.hMid.cMid.h 模板mian.cApp.cApp.hCom.cCom.hDir.cDir…

踩坑記錄:RecyclerView 局部刷新notifyItemChanged多次調用只觸發一次 onBindViewHolder 的原因

1. 問題背景 在做項目的時候&#xff0c;RecyclerView需要使用局部刷新&#xff0c;使用 notifyItemChanged(position, payload) 實現局部刷新&#xff0c;但發現調用多次只執行了一次&#xff0c;第二個刷新不生效。 2. 錯誤示例&#xff08;只處理 payloads.get(0)&#xff…

OpenLayers 加載鷹眼控件

注&#xff1a;當前使用的是 ol 5.3.0 版本&#xff0c;天地圖使用的key請到天地圖官網申請&#xff0c;并替換為自己的key 地圖控件是一些用來與地圖進行簡單交互的工具&#xff0c;地圖庫預先封裝好&#xff0c;可以供開發者直接使用。OpenLayers具有大部分常用的控件&#x…

WPF···

設置啟動頁 默認最后一個窗口關閉,程序退出,可以設置 修改窗體的icon圖標 修改項目exe圖標 雙擊項目名會看到代碼 其他 在A窗體點擊按鈕打開B窗體,在B窗體設置WindowStartupLocation=“CenterOwner” 在A窗體的代碼設置 B.Owner = this; B.Show(); B窗體生成在A窗體中間…

github公開項目爬取

import requestsdef search_github_repositories(keyword, tokenNone, languageNone, max_results1000):"""通過 GitHub API 搜索倉庫&#xff0c;支持分頁獲取所有結果&#xff08;最多 1000 條&#xff09;:param keyword: 搜索關鍵詞:param token: GitHub To…