數據結構——順序表和單向鏈表(2)

目錄

前言

一、單向鏈表

1、基本概念

2、單向鏈表的設計

(1)節點設計

(2)初始化空單向鏈表

(3)、初始化數據節點

(4)數據節點

(5)判斷鏈表是否為空

(6)遍歷鏈表

(7)刪除數據

(8)銷毀整個鏈表

(9)修改數據


前言

????????在軟件的世界里,程序 = 數據結構 + 算法。這句廣為流傳的名言,深刻地揭示了數據結構的核心地位。它是我們組織、存儲和管理數據的藝術,是構建高效、穩定應用程序的基石。而在眾多數據結構中,線性表作為最基礎、最常用的一種,其重要性不言而喻。

????????順序表和單向鏈表,正是線性表兩種最經典的實現方式。它們如同“一體兩面”,代表了計算機中兩種最根本的物理存儲思想:連續的空間離散的空間

  • 順序表憑借其底層連續的物理結構,帶來了無與倫比的隨機訪問性能,仿佛一本頁碼清晰的書籍,我們可以根據目錄瞬間翻到任何一頁。然而,這種連續性的代價便是在中間進行插入或刪除時,可能引發大規模的“數據搬遷”,效率堪憂。

  • 單向鏈表則巧妙地利用指針,將離散的內存空間“串聯”起來。它犧牲了隨機訪問的能力,換來了在任意位置高效插入與刪除的靈活性,如同一條環環相扣的鏈條,只需改變節點的指向,即可輕松完成結構的調整。

????????理解二者的內在原理、優缺點以及適用場景,是每一位開發者內功修煉的必經之路。這不僅是為了應對面試官的考問,更是為了在未來的開發中,能夠根據實際需求,做出最合理的技術選型,寫出性能更優、更優雅的代碼。


一、單向鏈表

1、基本概念

概念:????????

????????鏈式存儲的線性表,簡稱鏈表(線性關系+鏈式存儲)

圖解:

說明:

????????既然順序存儲中的數據因為擠在一起而導致需要成片移動,那很容易想到的解決方案是將數據離散的存儲在不同的內存塊中,然后用指針將它們串起來,這種樸素的思路所形成的鏈式線性表,就是所謂鏈表(單向鏈表(單向循環鏈表)、雙向鏈表(雙向循環鏈表))


2、單向鏈表的設計

1、節點設計
2、初始化空單向鏈表(初始化頭節點)
3、初始化數據節點
4、增刪查改單向鏈表// 前提:判斷鏈表是否為空a、增:頭插法、尾插法b、查:遍歷鏈表c、刪:刪除鏈表中的某個數據、銷毀整個鏈表d、改:修改鏈表中的某個數據

(1)節點設計

說明:

? ? ? ? 單向鏈表節點包含兩個域,一個數據域一個指針域,數據域存放用戶的數據,指針域指向本節點或指向相鄰下一個節點。

typedef struct node
{// 數據域int data;  // 指針域struct node* next_p;    // 指向相鄰的下一個節點的指針}node_t, *node_p;

(2)初始化空單向鏈表

概念:

????????首先鏈表有兩種常見的形式,一種是帶有所謂的頭節點的,一種是不帶頭節點的,所謂的頭節點是不存放有效數據的節點,僅僅用來方便操作

圖解:

????????

注意:

????????頭節點head_node是可選的,為了方便某些操作,建議大家有頭節點好一些

????????頭節點的指針:head_node的next_p

說明:

????????由于頭節點不存放有效數據的,因此如果空鏈表中帶有頭節點,那么頭指針(head_node的next_p)將永遠不變,這會給以后的鏈表操作帶來些許便捷

圖解:

示例代碼:

/*** @brief  初始化空單向鏈表(初始化頭節點)* @note   None* @param  None* @retval 成功:返回指向這個頭節點的指針*         失敗:返回NULL
*/
node_p LINK_LIST_InitHeadNode(void)
{// 1、給頭節點申請一個內存空間(堆內存)node_p p = malloc(sizeof(node_t));bzero(p, sizeof(node_t));// 2、將頭節點的next_p指針指向NULLif ( p!= NULL){// 數據域// 指針域p->next_p = NULL;   // 防止指針亂指}else{return NULL;}// 3、成功返回頭節點return p;
}

(3)、初始化數據節點

說明:

? ? ? ? 數據節點

圖解:

示例代碼:

????????

/*** @brief  初始化數據節點* @note   None* @param  data:數據節點中的數據* @retval 成功:返回指向這個數據節點的指針*         失敗:返回NULL
*/
node_p LINK_LIST_InitDataNode(int data)
{// 1、給數據節點申請一個內存空間(堆內存)node_p p = malloc(sizeof(node_t));bzero(p, sizeof(node_t));// 2、將數據節點的next_p指針指向NULL,并且將傳進來的數據對此節點的數據進行賦值if ( p != NULL){// 數據域p->data   = data;   // 數據賦值// 指針域p->next_p = NULL;   // 防止指針亂指}else{return NULL;}// 3、成功返回數據節點return p;}

(4)數據節點

說明:

????????將數據節點插入到鏈表中,一種頭插法(鏈表中的頭節點的后面插進去),一種尾插法(鏈表中的最后一個節點后面插入進去)

圖解:

頭插法:

尾插法:

示例代碼:

/*** @brief  插入數據(頭插法)* @note   None* @param  head_node:頭節點*         new_node: 要插入的數據節點* @retval None
*/
void LINK_LIST_InsertHeadDataNode(node_p head_node, node_p new_node)
{// 1、先讓new_node里面的next_p指向data_node  (head_node->next_p里面有data_node的地址)new_node->next_p = head_node->next_p;// 2、再讓head_node里面的next_p指向new_nodehead_node->next_p = new_node;
}/*** @brief  插入數據(尾插法)* @note   None* @param  head_node:頭節點    *         new_node: 要插入的數據節點* @retval None
*/
void LINK_LIST_TailInsertDataNode(node_p head_node, node_p new_node)
{// 1、設置一個中間指針,將指針指向單向鏈表的末尾節點node_p tmp_p = NULL;for (tmp_p = head_node; tmp_p->next_p != NULL; tmp_p = tmp_p->next_p);// 2、讓tmp_p的next_p指向新節點tmp_p->next_p = new_node;// 3、再讓new_node的next_p指向NULLnew_node->next_p = NULL;
}

(5)判斷鏈表是否為空

說明:

????????如果頭節點的next_p指向的是NULL,那么就可以證明這個鏈表是空的

圖解:

示例代碼:

/*** @brief  判斷鏈表是否為空* @note   None* @param  head_node:頭節點    * @retval 如果鏈表為空:返回true*         如果鏈表非空:返回false
*/
bool LINK_LIST_IfEmpty(node_p head_node)
{return head_node->next_p == NULL;
}

(6)遍歷鏈表

說明:

????????遍歷整個鏈表,并逐個打印里面的數據

圖解:

示例代碼:

/*** @brief  遍歷整個鏈表并打印出里面的數據* @note   None* @param  head_node:頭節點    * @retval 成功:返回0*         失敗:返回-1
*/
int LINK_LIST_ShowListData(node_p head_node)
{// 1、判斷鏈表是否為空,是的話,返回-1if (LINK_LIST_IfEmpty(head_node))return -1;// 2、遍歷整個鏈表,并逐個打印里面的數據node_p tmp_p = NULL;int    i     = 0;printf("======================鏈表中的數據==========================\n");for ( tmp_p = head_node->next_p; tmp_p!=NULL; tmp_p=tmp_p->next_p){printf("鏈表中的第%d的節點, 數據為: %d\n", i, tmp_p->data);i++;}printf("===========================================================\n");// 3、成功返回0return 0;
}

(7)刪除數據

說明:

????????根據數據來刪除鏈表中的節點

圖解:

示例代碼:

/*** @brief  刪除數據* @note   根據數據的值找到鏈表中的節點,并將其刪除* @param  head_node:頭節點  *         del_data: 要刪除的數據   * @retval 成功:返回0*         失敗:返回-1
*/
int LINK_LIST_DelDataNode(node_p head_node, int del_data)
{// 1、判斷鏈表是否為空,是的話,返回-1if (LINK_LIST_IfEmpty(head_node))return -1;// 2、移動到要刪除的節點數據那里去node_p tmp_p     = NULL;node_p last_node = NULL;node_p del_node  = NULL;node_p next_node = NULL;// a、從頭到尾遍歷一遍,找到要刪除的數據for (tmp_p = head_node; tmp_p->next_p!=NULL; tmp_p=tmp_p->next_p){// b、判斷要刪除的數據if ( (tmp_p->next_p->data) == del_data){// c、保存刪除數據的上一個節點和下一個節點、及刪除節點last_node = tmp_p;del_node  = tmp_p->next_p;next_node = del_node->next_p; break;}}// 3、繞過原鏈表要刪除的節點last_node->next_p = next_node;del_node->next_p  = NULL;// 4、釋放要刪除節點的資源free(del_node);// 5、成功返回0return 0;}

(8)銷毀整個鏈表

說明:

????????從頭節點開始,將其后面的數據節點一個個刪除,最后再刪除頭節點本身

圖解:

示例代碼:

/*** @brief  銷毀鏈表* @note   None* @param  head_node:頭節點    * @retval None
*/
void LINK_LIST_UnInit(node_p head_node)
{// 1、如果鏈表為空,那么直接釋放頭節點空間即可if (LINK_LIST_IfEmpty(head_node)){free(head_node);return;}// 2、node_p tmp_p      = NULL;               // 遍歷node_p last_node  = head_node;node_p del_node   = head_node->next_p;node_p next_node  = del_node->next_p;int num = 0;// 如果鏈表只有一個數據節點if (next_node == NULL){last_node->next_p = next_node;free(del_node);}// 否則else{for (tmp_p = head_node; tmp_p->next_p!=NULL; tmp_p=tmp_p->next_p){// a、刪除節點并釋放其內存last_node->next_p = next_node;free(del_node);printf("num == %d\n", num);num++;// b、輪回繼續del_node  = next_node;next_node = next_node->next_p;}// 少刪除了一個節點數據last_node->next_p = next_node;free(del_node);}// 3、釋放頭節點free(head_node);  
}

(9)修改數據

說明:

????????通過數據值,找到鏈表中的節點,并修改里面的數據

圖解:

示例代碼:

/*** @brief  修改數據* @note   根據數據的值來找到鏈表中的節點,對里面的數據進行修改* @param  head_node:  頭節點  *         data:       要修改的數據*         change_data:修改的數據* @retval 成功:返回0*         失敗:返回-1
*/
int LINK_LIST_ChangeNodeData(node_p head_node, int data, int change_data)
{// 1、如果鏈表為空,那么直接釋放頭節點空間即可if (LINK_LIST_IfEmpty(head_node))return -1;// 2、遍歷整個鏈表,找到數據的節點,并修該節點的相應的數據node_p tmp_p = NULL;// a、遍歷所有可能for (tmp_p = head_node->next_p; tmp_p!=NULL; tmp_p=tmp_p->next_p){// b、找到要修改的數據if ((tmp_p->data) == data)      // 根據學號{tmp_p->data = change_data;  // 修改這個學生的其它信息break;}   }// 3、成功返回0return 0;
}

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

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

相關文章

More Effective C++ 條款26:限制某個類所能產生的對象數量

More Effective C 條款26:限制某個類所能產生的對象數量核心思想:通過控制類的實例化過程,限制程序中該類的對象數量,可以防止資源過度使用,確保系統資源合理分配,并實現單例或有限實例模式。 &#x1f680…

CMS系統維護中常見的安全威脅及防護指南!

內容管理系統(CMS)已成為網站建設的核心工具,但隨之而來的安全風險卻常被低估。超過70%的網站使用CMS構建,而其中近半數曾遭遇安全漏洞威脅。作為運維人員和開發者,了解這些安全威脅并采取相應防護措施至關重要。 一、…

springboot knife4j 接口文檔入門與實戰

Spring Boot3 Knife4j 項目地址https://gitee.com/supervol/loong-springboot-study(記得給個start,感謝)Knife4j 介紹在國內 Java 開發領域,Knife4j 是一款廣受歡迎的 API 文檔工具,它基于 OpenAPI 規范,在…

Spring Boot 事務失效的八大原因及解決方案詳解

在 Spring Boot 項目開發中,聲明式事務管理通過 Transactional 注解提供了極大的便利。但許多開發者都曾遇到過事務不生效的困擾。本文將詳細分析導致 Spring Boot 事務失效的八大常見情況,并提供相應的解決方案。1. 數據庫引擎不支持事務問題分析&#…

數據結構:順序棧與鏈棧的原理、實現及應用

數據結構詳解:順序棧與鏈棧的原理、實現及應用 1. 引言:棧的核心概念 棧(Stack)是一種重要的線性數據結構,它遵循后進先出(Last In First Out, LIFO)的原則。這意味著最后一個被添加到棧中的元素…

apipost 8.x 腳本循環調用接口

apipost 8.x 腳本循環調用接口背景實現先說整體邏輯:最后背景 上周為了找某OA 偶爾出現的詭異現象,需要用測試工具來壓測,看看這個問題能否重現。以前用過Jmeter,但是沒有裝,正好有個國產的apipost看看如何&#xff1…

STM32 - Embedded IDE - GCC - 使用 GCC 鏈接腳本限制 Flash 區域

導言如上所示,Keil限制flash區域只需要在IROM1里將Start框框與Size框框填入具體信息即可。比如bootloader程序一般從0x8000000開始,大小0x10000(64KB)。此時,flash的范圍被限制在0x8000000 ~ 0x800FFFF。 另外&#xf…

Jenkins和Fastlane的原理、優缺點、用法、如何選擇

Jenkins 和 Fastlane 是軟件開發中用于自動化流程的工具一、Jenkins實現自動化打包1.1具體實現步驟安裝與配置:首先在服務器上安裝 Jenkins,可以通過官方提供的安裝包進行安裝,支持多種操作系統。安裝完成后,通過 Web 界面進行初始…

DOM常見的操作有哪些?

1.DOM文檔對象模型(DOM)是HTML和XML文檔的編程接口它提供了對文檔結構化表述,并定義了一種方式可以使從程序中對該結構進行訪問,從而改變文檔的結構,樣式和內容任何HTML或XML文檔都可以用DOM表示一個由節點構成的層級結…

【Kubernetes】知識點3

25. 說明Job與CronJob的功能。答:Job:一次性作業,處理短暫的一次性任務,僅執行一次,并保證處理的一個或者多個 Pod 成功結束。CronJob:周期性作業,可以指定每過多少周期執行一次任務。26. Kuber…

LINUX-網絡編程-TCP-UDP

1.目的:不同主機,進程間通信。2.解決的問題1)主機與主機之間物理層面必須互相聯通。2)進程與進程在軟件層面必須互通。IP地址:計算機的軟件地址,用來標識計算機設備MAC地址:計算機的硬件地址&am…

目標檢測定位損失函數:Smooth L1 loss 、IOU loss及其變體

Smooth L1 Loss 概述 Smooth L1 Loss(平滑 L1 損失),是一個在回歸任務,特別是計算機視覺中的目標檢測領域(如 Faster R-CNN, SSD)非常核心的損失函數。 xxx 表示模型的預測值,yyy 表示真實值&am…

Android開發之fileprovider配置路徑path詳細說明

第一步在清單文件配置fileprovider屬性<providerandroid:name"androidx.core.content.FileProvider"android:authorities"${applicationId}.fileprovider"android:exported"false"android:grantUriPermissions"true"><meta-d…

【ComfyUI】圖像描述詞潤色總結

在 ComfyUI 的工作流中&#xff0c;圖像反推描述詞能幫我們從圖像里抽取語義信息&#xff0c;但這些原始描述往往還顯得生硬&#xff0c;缺乏創意或流暢性。為了讓提示詞更自然、更有表現力&#xff0c;就需要“潤色”環節。潤色節點的任務&#xff0c;不是重新生成描述&#x…

java面試中經常會問到的IO、NIO問題有哪些(基礎版)

文章目錄一、IO 基礎與分類二、NIO 核心組件與原理三、NIO 與 BIO 的實戰對比四、AIO 與 NIO 的區別五、Netty 相關&#xff08;NIO 的高級應用&#xff09;總結Java 中的 IO&#xff08;輸入輸出&#xff09;和 NIO&#xff08;非阻塞 IO&#xff09;是面試中的重要考點&#…

時序數據庫選型指南:如何為工業場景挑選最強“數據底座”

工業4.0時代&#xff0c;工廠化身為巨大的數據生產中心。數以萬計的傳感器、PLC和設備每時每刻都在產生著海量的時間序列數據&#xff08;Time-Series Data&#xff09;&#xff1a;溫度、壓力、流速、振動、設備狀態……這些帶時間戳的數據是工業互聯網的血液&#xff0c;蘊含…

【排序算法】冒泡 選排 插排 快排 歸并

一、冒泡排序// 冒泡排序var bubbleSort function (arr) {const len arr.length;for (let i 0; i < len; i) {let isSwap false;for (let j 0; j < len - 1; j) {// 每一次遍歷都要比較相鄰元素的大小&#xff0c;如果滿足條件就交換位置if (arr[j] > arr[j 1])…

電子病歷空缺句的語言學特征描述與自動分類探析(以GPT-5為例)(中)

語言學特征刻畫(特征庫) 句法特征 句法特征是識別 SYN 類電子病歷空缺句的核心語言學維度,其量化分析通過構建依存句法結構的形式化指標,實現對語法不完整性的客觀描述。該類特征主要包括依存樹不完備指標、謂詞-論元覆蓋率及從屬連詞未閉合三類核心參數,共同構成 SYN 類…

InnoDB存儲引擎-事務

1. 事務概述事務可由一條簡單的SQL語句組成,也可以由一組復雜的SQL語句組成. 事務是訪問并更新數據庫中各種數據項的一個程序執行單元. 在事務中的操作, 要么都做修改, 要么都不做. 對于 InnoDB存儲引擎而言, 其默認的事務隔離級別 RR , 完全遵循和滿足了事務的 ACID 特性. 1.1…

web項目的目錄結構

web項目的目錄結構 WEB-INF 存放class文件、jar文件和配置文件&#xff0c;對于用戶來說該文件夾是不可見的WEB-INF/web.xml web應用程序的描述文件&#xff0c;用來配置資源&#xff0c;如servlet、過濾器、監聽器等WEB-INF/classes 用于存放class文件&#xff0c;也是該web應…