數據結構之鏈表(單向鏈表與雙向鏈表)

一,鏈表描述

鏈表是一種常見的重要的數據結構,是動態地進行存儲分配的一種結構。常用于需存儲的數據的數目無法事先確定。

1.鏈表的一般結構

鏈表的組成:
頭指針:存放一個地址,該地址指向一個元素
結點:用戶需要的實際數據和鏈接節點的指針

2.結點結構體類型的定義

鏈表結點結構的 一般形式:

struct  結構體名{  數據成員表;struct  結構體名  *指針變量名;};

3.結點的動態分配

形式是:malloc(存儲區字節數)
該函數返回存儲區的首地址。

釋放存儲區用如下函數:?free(p); 它表示釋放由p指向的存儲空間。

用結構體建立鏈表:

struct student{  int num;float score;struct student *next ;};

其中成員num和score用來存放結點中的有用數據(用戶需要用到的數據),next是指針類型的成員,它指向struct student類型數據(這就是next所在的結構體類型)

二,鏈表的設計與實現

1.單向鏈表

1.1 單向鏈表的組成:

? ? ? ? 1)頭指針
2)結點
數據域 ? (保存實際數據)
指針域 ? (保存下一個結點地址)

1.2 ?單向鏈表的設計實現:

1)定義結點類型
typedef int data_t;
typedef strcut  node
{data_t  data;strcut node *next;
}node_t;
2)單向鏈表功能算法實現

? ? ? (1)?鏈表創建
int slist_create(node_t**,data_t);
(2)? 數據添加
(2.1) ?頭插
int slist_addhead(node_t**,data_t)
(2.2) ?尾插
int slist_addtail(node_t**,data_t)
(2.3) ?中間插入
int slist_insert(node_t**,data_t pos,data_t new);
(3) ?數據刪除
int ?slist_delete(node_t**,data_t);
(4) ?數據查詢
node_t* slist_query(node_t*,data_t);?
(5) ?數據更新
int ?slist_update(node_t*,data_t old,data_t new);
(6) ?數據遍歷
void slist_showall(node_t*);
(7) ?鏈表回收
void slist_destroy(node_t**);

1.3 單向鏈表示例程序

該示例程序涉及到鏈表創建、數據添加(頭插、尾插、中間插入)、數據刪除、數據查詢、數據更新、數據遍歷、鏈表回收。

slist.c

#include <stdio.h>
#include <stdlib.h>typedef int data_t;typedef struct node
{data_t data;struct node *next;
} node_t;/* 1. 鏈表創建(創建一個空頭指針,返回成功與否) */
int slist_create(node_t **head, data_t value)
{*head = (node_t *)malloc(sizeof(node_t));if (*head == NULL){perror("malloc");return -1;}(*head)->data = value;(*head)->next = NULL;return 0;
}/*頭插法 */
int slist_addhead(node_t **head, data_t value)
{node_t *new = (node_t *)malloc(sizeof(node_t));if (!new){perror("malloc");return -1;}new->data = value;new->next = *head;*head = new;return 0;
}/*尾插法 */
int slist_addtail(node_t **head, data_t value)
{node_t *new = (node_t *)malloc(sizeof(node_t));if (!new){perror("malloc");return -1;}new->data = value;new->next = NULL;if (*head == NULL){*head = new;}else{node_t *p = *head;while (p->next != NULL)p = p->next;p->next = new;}return 0;
}/*中間插入:在指定數據 pos 后插入 new */
int slist_insert(node_t **head, data_t pos, data_t newval)
{node_t *p = *head;while (p && p->data != pos){p = p->next;}if (!p){printf("未找到插入位置 %d\n", pos);return -1;}node_t *new = (node_t *)malloc(sizeof(node_t));if (!new){perror("malloc");return -1;}new->data = newval;new->next = p->next;p->next = new;return 0;
}/* 3. 刪除結點(按值刪除) */
int slist_delete(node_t **head, data_t value)
{node_t *p = *head, *q = NULL;while (p && p->data != value){q = p;p = p->next;}if (!p){printf("未找到要刪除的值 %d\n", value);return -1;}if (q == NULL)*head = p->next; // 刪除頭結點elseq->next = p->next;free(p);return 0;
}/* 4. 查詢(返回結點指針) */
node_t *slist_query(node_t *head, data_t value)
{while (head){if (head->data == value)return head;head = head->next;}return NULL;
}/* 5. 更新 */
int slist_update(node_t *head, data_t old, data_t newval)
{node_t *p = slist_query(head, old);if (!p){printf("未找到需要更新的值 %d\n", old);return -1;}p->data = newval;return 0;
}/* 6. 遍歷 */
void slist_showall(node_t *head)
{printf("鏈表內容: ");while (head){printf("%d -> ", head->data);head = head->next;}printf("NULL\n");
}/* 7. 鏈表回收 */
void slist_destroy(node_t **head)
{node_t *p = *head;while (p){node_t *tmp = p;p = p->next;free(tmp);}*head = NULL;
}/* ============= 測試主函數 ============= */
int main(void)
{node_t *head = NULL;/* 創建鏈表 */slist_create(&head, 10);slist_addhead(&head, 5);slist_addtail(&head, 20);slist_addtail(&head, 30);slist_insert(&head, 20, 25); // 在20后插入25slist_showall(head);/* 刪除 */slist_delete(&head, 10);slist_showall(head);/* 查詢并更新 */node_t *res = slist_query(head, 25);if (res)printf("找到結點: %d\n", res->data);slist_update(head, 25, 99);slist_showall(head);/* 回收 */slist_destroy(&head);slist_showall(head);return 0;
}

運行結果:

1.4 鏈表數據結構的小結

?? ? ? ??應用場景:?
1)鏈式結構是一種動態增加數據的結構,如果存儲的數據數量事先無法確定,使用鏈表是比較合適的;
2)常用于對數據進行頻繁的增加或者刪除的情況

?? ??? ? 缺點:
查詢效率比較低,主要原因是鏈表數據必須從頭結點開始遍歷。

2. 雙向鏈表設計實現

說明:雙向鏈表是在單向鏈表的基礎上,指針域增加了存儲上一個結點地址的指針變量(前驅指針)
優勢:由于雙向鏈表具有后繼指針,也有前驅指針,所以對數據的增加,刪除更加方便快捷,原因是鏈表結點的插入和刪除,往往會影響上一個結點,相比于單向鏈表需要查找上一個結點,雙向鏈表就可以通過結點的前驅指針直接獲取。

2.1雙向鏈表的組成

? ? ? ?1) ? 頭指針
2) ? 結點:
數據域 ? (保存實際數據)
指針域 ? (保存下一個結點地址) ?
(保存上一個結點地址)?

2.2 ?雙向鏈表的設計實現:

1) ?定義結點類型:? ? ? ? ??

?typedef int data_t;typedef strcut node{data_t ?data;strcut ?node ? *prev;strcut ?node ? *next;}node_t;

?? ??? ? ? ?2) ?雙向鏈表功能算法實現:
(1) ?鏈表創建
int dlist_create(node_t**,data_t);
(2)?數據添加
(2.1) ?頭插
int dlist_addhead(node_t**,data_t)
(2.2) ?尾插
int dlist_addtail(node_t**,data_t)
(2.3) ?中間插入
int dlist_insert(node_t** head,data_t pos,data_t new);
(3) ?數據刪除
int ?dlist_delete(node_t**,data_t);
(4) ?數據查詢
node_t* dlist_query(node_t*,data_t);?
(5) ?數據更新
int ?dlist_update(node_t*,data_t old,data_t new);
(6) ?數據遍歷
void dlist_showall(node_t*);
(7) ?鏈表回收
void dlist_destroy(node_t**);


2.3 示例程序:

dlist.c

#include <stdio.h>
#include <stdlib.h>typedef int data_t;// 定義雙向鏈表結點
typedef struct node {data_t data;struct node *prev;struct node *next;
} node_t;/*鏈表創建 */
int dlist_create(node_t **head, data_t value) {*head = (node_t *)malloc(sizeof(node_t));if (*head == NULL) return -1;(*head)->data = value;(*head)->prev = NULL;(*head)->next = NULL;return 0;
}/*頭插 */
int dlist_addhead(node_t **head, data_t value) {node_t *newnode = (node_t *)malloc(sizeof(node_t));if (newnode == NULL) return -1;newnode->data = value;newnode->prev = NULL;newnode->next = *head;if (*head != NULL) {(*head)->prev = newnode;}*head = newnode;return 0;
}/*尾插 */
int dlist_addtail(node_t **head, data_t value) {node_t *newnode = (node_t *)malloc(sizeof(node_t));if (newnode == NULL) return -1;newnode->data = value;newnode->next = NULL;if (*head == NULL) {newnode->prev = NULL;*head = newnode;return 0;}node_t *p = *head;while (p->next != NULL) {p = p->next;}p->next = newnode;newnode->prev = p;return 0;
}/*中間插入(在pos位置前插入新節點) */
int dlist_insert(node_t **head, data_t pos, data_t value) {node_t *p = *head;while (p != NULL && p->data != pos) {p = p->next;}if (p == NULL) return -1;node_t *newnode = (node_t *)malloc(sizeof(node_t));if (newnode == NULL) return -1;newnode->data = value;newnode->next = p;newnode->prev = p->prev;if (p->prev != NULL) {p->prev->next = newnode;} else {*head = newnode;  // 插在頭部}p->prev = newnode;return 0;
}/*刪除節點(刪除值為value的第一個節點) */
int dlist_delete(node_t **head, data_t value) {node_t *p = *head;while (p != NULL && p->data != value) {p = p->next;}if (p == NULL) return -1;if (p->prev != NULL) {p->prev->next = p->next;} else {*head = p->next;  // 刪除頭節點}if (p->next != NULL) {p->next->prev = p->prev;}free(p);return 0;
}/*數據查詢 */
node_t* dlist_query(node_t *head, data_t value) {node_t *p = head;while (p != NULL) {if (p->data == value) {return p;}p = p->next;}return NULL;
}/*數據更新 */
int dlist_update(node_t *head, data_t old, data_t new) {node_t *p = head;while (p != NULL) {if (p->data == old) {p->data = new;return 0;}p = p->next;}return -1;
}/*遍歷(正向) */
void dlist_showall(node_t *head) {node_t *p = head;printf("DList: ");while (p != NULL) {printf("%d ", p->data);p = p->next;}printf("\n");
}/*鏈表回收 */
void dlist_destroy(node_t **head) {node_t *p = *head;while (p != NULL) {node_t *tmp = p;p = p->next;free(tmp);}*head = NULL;
}/* 測試主函數 */
int main() {node_t *head = NULL;// 創建鏈表dlist_create(&head, 10);dlist_addhead(&head, 5);dlist_addtail(&head, 20);dlist_addtail(&head, 30);dlist_insert(&head, 20, 15);  // 在20前插入15dlist_showall(head);// 查詢node_t *q = dlist_query(head, 15);if (q) printf("Found: %d\n", q->data);// 更新dlist_update(head, 15, 18);dlist_showall(head);// 刪除dlist_delete(&head, 10);dlist_showall(head);// 回收dlist_destroy(&head);return 0;
}

運行結果:

鏈表小結:

1. 定義

  • 單向鏈表(Singly Linked List)
    每個節點只有一個指針 next,指向下一個節點。
    結構簡單,內存開銷小,但只能 單方向遍歷

  • 雙向鏈表(Doubly Linked List)
    每個節點有兩個指針:prev(前驅)和 next(后繼)。
    可以 雙向遍歷,刪除/插入更方便,但每個節點占用內存更大。

2. 節點結構

單向鏈表

typedef int data_t;
typedef struct node {data_t data;struct node *next;
} node_t;

雙向鏈表

typedef int data_t;
typedef struct node {data_t data;struct node *prev;struct node *next;
} node_t;

3. 基本功能函數

功能單向鏈表 (slist)雙向鏈表 (dlist)
創建鏈表slist_create()dlist_create()
頭插入slist_addhead()dlist_addhead()
尾插入slist_addtail()dlist_addtail()
中間插入slist_insert()dlist_insert()
刪除節點slist_delete()dlist_delete()
查詢節點slist_query()dlist_query()
更新節點slist_update()dlist_update()
遍歷輸出slist_showall() (等同于 print)dlist_showall() (等同于 print)
銷毀鏈表slist_destroy()dlist_destroy()

4. 主要區別

對比點單向鏈表雙向鏈表
內存占用小,每個節點只需 next 指針大,每個節點需 prev + next
遍歷方向只能從頭到尾可從頭到尾,也能從尾到頭
插入效率需要找到插入點的前驅節點直接利用 prev / next 修改指針,效率更高
刪除效率需要找到刪除節點的前驅節點直接用 prev / next 修改即可
實現復雜度簡單稍復雜
適用場景內存緊張、簡單隊列/棧結構頻繁插入/刪除、需要雙向遍歷

5. 輸出函數

單向鏈表輸出

void slist_showall(node_t* head) {node_t* p = head;while (p) {printf("%4d\n", p->data);  // 每行一個,右對齊p = p->next;}
}

雙向鏈表輸出

void dlist_showall(node_t* head) {node_t* p = head;while (p) {printf("%4d\n", p->data);p = p->next;}
}

總結:

  • 單向鏈表:結構簡單,適合空間敏感、邏輯簡單的場景(如棧、隊列)。

  • 雙向鏈表:操作靈活,適合需要頻繁插入/刪除和雙向遍歷的場景(如 LRU 緩存、雙端隊列)。

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

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

相關文章

從反向代理到負載均衡:Nginx + Tomcat 構建高可用Web服務架構

從反向代理到負載均衡&#xff1a;Nginx Tomcat 構建高可用Web服務架構 文章目錄從反向代理到負載均衡&#xff1a;Nginx Tomcat 構建高可用Web服務架構一、基礎鋪墊&#xff1a;什么是反向代理&#xff1f;1.1 反向代理的核心原理1.2 Nginx反向代理實戰配置步驟1&#xff1a…

Simulink中使用Test sequence單元測試

一、Tips 在對simulink模型進行Test sequence單元測試時&#xff0c;如果采取書寫測試用例的話&#xff0c;有以下操作。 1、使用”fprintf(‘time%f\n’, t);“來打印當前step的時間&#xff1b; 二、數據類型轉換 1、double類型 -> boolean類型 clc; clear all;% 1、doubl…

【mysql】SQL自連接:什么時候需要,什么時候不需要?

SQL自連接:什么時候需要,什么時候不需要? 通過具體示例和對比解析,徹底搞懂SQL自連接的使用場景 在處理SQL查詢時,尤其是當表中存在自引用關系(如referee_id引用同一張表的id)時,很多開發者會疑惑:這個查詢到底需不需要自連接?本文將通過多個具體示例,帶你徹底弄清何…

「美」創新在于人,而不是產品 - AxureMost 落葵網

添加圖片注釋&#xff0c;不超過 140 字&#xff08;可選&#xff09; 第一章&#xff1a;創新的心理學 創新與心理安全 蠟燭問題&#xff1a;卡爾鄧克爾的蠟燭問題實驗揭示了創造性思維的重要性。通過顛覆對盒子用途的先入為主觀念&#xff0c;參與者能夠找到創新性的解決方案…

新規則,新游戲:AI時代下的戰略重構與商業實踐

當你的客服AI能夠真正像員工一樣理解客戶的行業術語&#xff0c;當AI能主動從大量的客戶咨詢中篩選出高價值潛在客戶 —— 這已經不再是理想中才能存在的場景&#xff0c;而是當下 “人工智能 ” 行動深入推進中&#xff0c;企業智能化轉型的真實寫照。 "人工智能 " …

ScanNet: Richly-annotated 3D Reconstructions of Indoor Scenes 數據集構建

paper link: paperlink Abstract: 這個數據集是個RGB-D視頻數據集&#xff0c;在707個不同空間中獲取了1513個掃描的場景&#xff0c;250w個視圖&#xff0c;并且標注了相機位姿&#xff0c;表面重建&#xff0c;語義分割。本數據集共有20人掃描500名工作者進行標注。 數據集…

c語言期末復習

一、選擇題(10道) 1、以下哪個不是C語言的關鍵字? A) int B) float C) string D) while (答案:C) 2、表達式 5 / 2 的結果是: A) 2.5 B) 2 C) 3 D) 2.0 (答案:B) 3、指針變量存儲的是: A) 變量的值 B) 變量的地址 C) 變量的類型 D) 變量的名稱 (答案:B) 4、以…

JLINK 調試器單步調試單片機

0 JLINK 調試器單步調試單片機 1 物理層1.1 調整電壓和開發板一致2 環境搭建 2.1 安裝 JLink_Windows_V862_x86_642.2 vscode 配置 {"version": "0.2.0","configurations": [{"name": "(gdb) 啟動","type": "…

大模型(LLM)安全保障機制(技術、標準、管理)

大模型&#xff08;LLM&#xff09;的安全保障涉及技術、標準、管理等多個層面。下面我將結合其核心風險&#xff0c;為你梳理主要的安全機制、相關標準框架以及一些實踐建議。為了讓您快速了解大模型面臨的主要風險及相應的應對機制&#xff0c;我準備了一個表格&#xff1a;安…

虛擬機之CentOS、網絡設置的有趣問題

前言 年初射出的子彈&#xff0c;今天中了。 年初埋下的坑&#xff0c;今年踩了。 回首過往&#xff0c;why&#xff1f; because&#xff1a;當時下載VMware的時候。沒有設置網絡。 重點——使用VMware安裝CentOS 9 使用VMware安裝CentOS Stream 9_嗶哩嗶哩_bilibili 總…

Biomni:來自斯坦福的通用型生物醫學 AI 智能體,科研“虛擬助手“來了!

在當今生物醫學研究中&#xff0c;實驗手段和數據量正以前所未有的速度膨脹。從基因組學、單細胞組學到多模態數據&#xff0c;再到可穿戴設備的健康監測&#xff0c;科研人員每天都在與龐大的數據和復雜的分析流程打交道。 然而&#xff0c;實驗設計瑣碎、工具分散、跨學科整合…

移植后 eto 陽性 干擾素 α1b、白介素 - 2 dli

在異基因造血干細胞移植&#xff08;allo-HSCT&#xff09;后仍存在 AML1-ETO&#xff08;ETO&#xff09;融合基因陽性的患者中&#xff0c;干擾素 α1b 聯合白介素 - 2&#xff08;IL-2&#xff09; 是臨床中探索用于清除微小殘留病&#xff08;MRD&#xff09;、降低復發風險…

防止接口被薅羊毛(防刷)(DAY 002)

背景&#xff1a;短信驗證碼接口被不法分子用來做灰產&#xff08;短信郵箱轟炸機&#xff09; 如何避免??的?站成為”?雞“或者被刷&#xff1f; 增加圖形驗證碼&#xff08;開發?員&#xff09;單IP請求次數限制&#xff08;開發?員&#xff09; 防刷之圖形驗證碼&…

【RabbitMQ】----RabbitMQ 的7種工作模式

1.Simple(簡單模式) P:?產者,也就是要發送消息的程序 C:消費者,消息的接收者 Queue:消息隊列,圖中??背景部分.類似?個郵箱,可以緩存消息;?產者向其中投遞消息,消費者從其中取出消息. 特點:?個?產者P&#xff0c;?個消費者C,消息只能被消費?次.也稱為點對點(Point-to-P…

今日分享:C++ -- list 容器

&#x1f60e;【博客主頁&#xff1a;你最愛的小傻瓜】&#x1f60e; &#x1f914;【本文內容&#xff1a;C list容器 &#x1f60d;】&#x1f914; --------------------------------------------------------------------------------------------------------------------…

【Python】數據可視化之分布圖

分布圖主要用來展示某些現象或數據在地理空間、時間或其他維度上的分布情況。它可以清晰地反映出數據的空間位置、數量、密度等特征&#xff0c;幫助人們更好地理解數據的內在規律和相互關系。 目錄 單變量分布 變量關系組圖 雙變量關系 核密度估計 山脊分布圖 單變量分布…

DDD+WebAPI實戰

DDD+WebAPI實戰 DDD(領域驅動設計,Domain-Driven Design)是一種面向對象的設計方法,它強調將業務邏輯封裝在模型中,并通過這些模型來驅動整個應用的設計。在.NET環境中,特別是在使用ASP.NET Core和Web API構建應用時,DDD可以幫助我們更好地組織代碼,使得業務邏輯更加清…

人力資源管理的思維方法學習筆記1

北京師范大學政府管理學院1.課程介紹&#xff1a; 講述視角上&#xff0c;本課程側重人力資源管理的思維方式&#xff0c;即人力資源管理理論和時間的不同視角和主導范式的分析。這既是對人力資源管理理論發展的凝練&#xff0c;也是對人力資源管理實踐演進過程的總結。對于把握…

適應新環境:Trae編輯器下的IDEA快捷鍵定制

介紹&#xff1a;學習如何在Trae編輯器中配置IntelliJ IDEA風格的快捷鍵&#xff0c;減少開發環境間的切換成本&#xff0c;提升編碼效率。通過安裝插件或手動調整&#xff0c;讓你更快適應新工具大家好&#xff0c;我是凱哥Java本文標簽&#xff1a;代碼編輯效率、Trae快捷鍵、…

基于YOLO8的汽車碰撞事故檢測系統【數據集+源碼+文章】

基于YOLOv8和Streamlit的汽車碰撞事故檢測系統 文末附下載地址 開發目的 隨著城市化進程的加快和機動車保有量的持續攀升&#xff0c;道路交通安全問題日益突出&#xff0c;汽車碰撞事故頻發不僅嚴重威脅駕乘人員的生命安全&#xff0c;也對公共秩序、應急響應效率及交通管理…