嵌入式第十八課!!數據結構篇入門及單向鏈表

在前幾章對C語言的學習中,我們學到了:

  1. 基本的C語法和簡單算法
  2. 面向過程的編程思想

而在數據結構這一篇章,我們將要學習:

  1. 常用的數據存儲結構
  2. 算法
  3. 面向對象的編程思想

數據結構

在正式開始學習之前,我們先來了解一下什么是數據結構:

什么是數據結構?

所謂數據結構,就是用來組織和存儲數據的,而存儲 , 即存儲變量、數組(在數據結構里稱為順序表)等;程序 = 數據結構 + 算法,了解數據的存放佐以數據的算法,就構成了程序。

數據與數據之間的關系

1)邏輯結構 :數據元素與元素之間的關系

  • ?集合:元素與元素之間平等獨立的集合關系
  • 線性結構:數據元素與元素之間存在一對一的關系(順序表、鏈表、隊列、棧)
  • 樹形結構:數據元素與元素之間存在一對多的關系(二叉樹)
  • 圖形結構:數據元素與元素之間存在多對多的關系 (網狀結構)

2)物理結構 :數據元素在計算機內存中的存儲方式

順序結構:

在內存中選用一段連續的內存空間(線性結構)進行存儲

  1. ?數據訪問方便(O(1))
  2. 插入和刪除數據時需要移動大量數據
  3. 需要預內存分配
  4. 可能造成大量的內存碎片??

內存碎片:

  • 內內存碎片:如結構體struct里空出的空間;
  • 外內存碎片:申請大連續空間(malloc)剩余的空間;

順序結構的存儲如圖所示:

像數組一樣,依次進入存儲空間,如果要插入數據或刪除數據都要把該數據前后的數據修改一下。

鏈式結構:

可以在內存中選用一段非連續的內存空間進行存儲

  1. 數據訪問時必須要從頭遍歷(O(n))
  2. 插入和刪除元素方便
  3. 不需要預內存分配,是一種動態存儲的方式
  4. 可以充分使用內存空間

鏈式結構的存儲方式如圖所示:

鏈式結構存儲是通過元素后的指針指向下一元素進行鏈接的,當最后一個元素后的指針是一個空指針時就表示停止。

順序結構和鏈式結構的時間復雜度如圖所示:

索引結構:

將要存儲的數據的關鍵字和存儲位置之間構建一個索引表,也就是給下面的哈希函數建立一個表。

散列結構(哈希結構):

將數據的存儲位置與數據元素之間的關鍵字建立起對應的關系(哈數函數),根據該關系進行數據存儲和快速查找

哈希結構存儲方式如下圖:

中間關鍵字key 和addr的表就是索引結構

基本功能

數據結構的基本功能就是:

  1. 快捷使用指針
  2. 結構體(封裝)
  3. 實現動態內存分配

數據結構的內容

數據結構包含一下內容(帶*的為重點)

? ? ?1. 鏈式表
單向鏈表*
雙向鏈表*
循環鏈表
內核鏈表
2. 棧*
3. 隊列*
4. 二叉樹
5. 哈希表
6.常用算法*

單向鏈表

單向鏈表的組成

鏈表:對象(存儲數據的對象)、屬性(變量)、行為(函數)

單向鏈表是由鏈表對象與各個結點組成的:

結點:

單向鏈表的結點是由上方的數據域data和下方的指針域pnext(指向下一個結點)組成的。

鏈表對象

鏈表對象是由phead(保存頭結點地址,指向下一節點),clen(節點個數),以及其他屬性組成的。

單向鏈表代碼

1. 創建鏈表對象

首先我們要先在聲明? link . h? 里封裝結構體:

#ifndef __LINK_H__
#define __LINK_H__/*鏈表存儲的數據的數據類型*/
typedef int Data_type_t;/*鏈表的結點類型*/
typedef struct node
{Data_type_t data;struct node *pnext;
}Node_t;/*鏈表對象類型*/
typedef struct link
{Node_t *phead;int clen;
}Link_t;
#endif

然后我們在函數文件里創建函數:

#include "link.h"
#include <stdlib.h>
#include <stdio.h>/*創建單向鏈表*/
Link_t *create_link()
{Link_t *plink = malloc(sizeof(Link_t));if (NULL == plink){printf("malloc error!\n");return NULL;}plink->phead = NULL;plink->clen = 0;return plink;
}

在堆區開辟空間創建鏈表對象,如果沒有申請到空間,即打印出錯;如果申請成功,將鏈表對象指向結點的指針初始化為空指針,節點個數設置為0;

在主函數調用函數:

#include <stdio.h>
#include "link.h"int main(int argc, const char *argv[])
{Link_t *plink = create_link();if (NULL == plink){return -1;}return 0;
}

2. 插入數據(頭插、尾插)

頭插

頭插即是從鏈表對象后開始插入數據:

/*向單向鏈表的頭部插入數據*/
int insert_link_head(Link_t *plink, Data_type_t data)
{//申請結點Node_t *pnode = malloc(sizeof(Node_t));if (NULL == pnode){printf("malloc error!");return -1;}//初始化結點pnode->data = data;pnode->pnext = NULL;//頭插2步插入結點pnode->pnext = plink->phead;plink->phead = pnode;plink->clen++;return 0;
}

首先,申請空間存放節點,后對結點進行初始化;我們要先讓待插入的結點指向當前鏈表對象指向的結點;后再讓鏈表對象指向當前待插入的結點。不然如果先讓鏈表對象指向待插入的結點,后一位結點的位置將會丟失,不能再進行鏈接:

尾插

尾插即是從鏈表末尾開始插入數據:

int insert_link_back(Link_t *plink , Data_type_t data)
{Node_t *pnode = malloc(sizeof(Node_t));if(NULL == pnode){printf("malloc error!");return -1;}pnode -> data = data;pnode -> pnext =NULL;if(plink -> phead == NULL){plink -> phead = pnode;pnode -> pnext = NULL;plink -> clen++;return 0;}else{Node_t *ptmp = plink -> phead;while(ptmp -> pnext != NULL){ptmp =  ptmp-> pnext;}ptmp -> pnext = pnode;plink ->clen++;return 0;}

尾插的一些邏輯算法和頭插一致,但不同的是,先判斷是否是一個空鏈表:如果是一個空鏈表,那就直接改變鏈表對象的指向與結點的指向即可;如果不是一個空指針,要先遍歷一遍結點,找到末尾的結點,修改它的指針域指向待插入結點。

3. 刪除數據(頭刪、尾刪)

頭刪

頭刪即是從鏈表對象指向的結點開始銷毀空間:

int delete_link(Link_t *plink)
{Node_t*ptmp = plink -> phead;plink -> phead = ptmp -> pnext;if(plink -> phead == NULL){return 0;}else{free(ptmp);plink -> clen--;ptmp = NULL;return 1;}
}

先設定一個指針來指向第一個結點,此時設定鏈表對象指向下一個結點;如果此時是一個空鏈表,即不再運行、如果非空鏈表,就解放當前指針ptmp指向的第一個結點的空間,同時讓節點個數-1,別忘了此時ptmp是野指針,將ptmp設為空指針。

尾刪

尾刪即是從鏈表的最后一個結點開始刪除:

int delete_linkback(Link_t *plink)
{if (plink -> phead == NULL){free(plink);return -1;}Node_t*ptmp = plink -> phead;if (ptmp -> pnext == NULL){delete_link(plink);return 0;}else{while(ptmp -> pnext -> pnext != NULL){ptmp = ptmp -> pnext;}free(ptmp -> pnext);plink -> clen--;ptmp -> pnext = NULL;return 1;}
}

這種刪除數據的方法要找到尾部結點的前一個結點,故使用ptmp尋找它指向結點的下一結點的指向是否指向空指針;如果不是,則繼續遍歷、如果是,則解放當前指針指向結點的下一結點;同時要記得判斷空鏈表與鏈表只有一個節點的情況;

4. 查找數據

查找數據時要遍歷所有結點的數據域,然后輸出找到的地址:

Node_t *find_tdata(Link_t *plink , Data_type_t n)
{Node_t *ptmp = plink->phead;while(ptmp != NULL){if(n == ptmp -> data){return ptmp;}ptmp = ptmp -> pnext;}return ptmp;}

這個只需要先讓指針指向第一個結點,后一一遍歷比較即可。

5. 修改數據

修改數據可以直接調用查找數據的函數,然后直接改變其結點的數據域即可:

Node_t *find_tdata(Link_t *plink , Data_type_t n)
{Node_t *ptmp = plink->phead;while(ptmp != NULL){if(n == ptmp -> data){return ptmp;}ptmp = ptmp -> pnext;}return ptmp;}
int change_linkdata(Link_t *plink , Data_type_t oldata , Data_type_t newdata)
{Node_t*ptmp = find_tdata(plink , oldata);if(ptmp == NULL){return 0;}else{ptmp -> data = newdata;return 1;}
}

6. 銷毀鏈表

銷毀鏈表不能直接使用free函數調用指向鏈表的指針plink,這樣會導致剩余的結點的位置全部丟失,導致剩下很多結點的存儲空間未被銷毀。所以要先使用刪除數據,銷毀所有結點空間,后再進行銷毀鏈表對象空間:

Link_t *destroy_link(Link_t *plink)
{int n;if(plink -> phead == NULL){free(plink);plink = NULL;return plink;}else{while(plink -> phead != NULL){n = delete_link(plink);if(n == 0){plink = NULL;return plink;}}}
}

調用刪除數據函數進行循環,直到鏈表變為空鏈表為止,后進行銷毀;

以上就是和大家分享的內容!!!!單向鏈表的理解需要指針扎實的基礎和結構體的應用,感謝你的閱讀!!!!如有遺漏和錯誤,歡迎評論區進行批評和指正!!

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

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

相關文章

win10任務欄出問題了,原來是wincompressbar導致的

問題描述兄弟們客戶說自己電腦現在有問題了&#xff0c;任務欄顯示的都不對&#xff0c;和之前的都不一樣&#xff0c;現在使用起來非常難受&#xff0c;我們來看一下&#xff0c;這到底是什么問題吧&#xff01;到客戶現場&#xff0c;查看發現&#xff0c;客戶桌面系統最底下…

FFmpegHandler 功能解析,C語言程序化設計與C++面向對象設計的核心差異

FFmpegHandler 功能解析 本文件記錄了關于 FFmpegHandler 類中核心函數工作流程的詳細解釋。Q: FFmpeg逐幀解碼&#xff0c;FFmpegHandler::openVideo 和 FFmpegHandler::readAVFrame 這兩個函數都分別做了什么&#xff1f; A: 可以把整個過程想象成“準備播放一部電影”&#…

Codeforces Round 1039 (Div. 2) A-C

A. Recycling Center題目大意 給你n個垃圾袋&#xff0c;每個垃圾袋有一個重量 在每秒鐘&#xff0c;你可以選擇一個垃圾袋&#xff0c;如果他的重量小于等于c&#xff0c;那么你可以不花費硬幣丟掉它 當你丟掉一個垃圾袋后&#xff0c;其他垃圾袋在這一秒重量會翻倍 問最少花費…

【設計模式】 原則

單一職責原則 對于一個類而言&#xff0c;有且僅有一個引起他變化的原因或者說&#xff0c;一個類只負責一個職責 如果一個類承擔的職責過多&#xff0c;那么這些職責放在一起耦合度太高了&#xff0c;一個職責的變化可能會影響這個類其他職責的能力。 所以我們在做軟件設計的時…

windows11右鍵菜單新增項增加drawio文件,使用draw.io

目錄1.新建空白模板2.建立注冊表文件1.新建空白模板 這里我們的模板文件路徑為 D:\Software\drawio\template.drawio 2.建立注冊表文件 首先新建一個.txt文件&#xff0c;我這里取名為menulize.txt&#xff0c;然后將下面的內容復制到.txt文件中 Windows Registry Editor Ver…

解鎖網頁魔法:零基礎HTML通關秘籍

文章目錄**解鎖網頁魔法&#xff1a;零基礎HTML通關秘籍**HTML 基礎目標HTML 結構認識 HTML 標簽HTML 文件基本結構標簽層次結構快速生成代碼框架HTML 常見標簽注釋標簽注釋的原則標題標簽: h1-h6段落標簽: p換行標簽&#xff1a;br綜合案例: 展示博客超鏈接標簽: a表格標簽**基…

類似 Pixso 但更側重「網頁 / 軟件界面設計」「前后端可視化開發」的工具

從 GoView 的 Demo 功能來看&#xff0c;它主要聚焦于數據可視化大屏的低代碼搭建&#xff0c;更側重數據圖表配置和頁面布局&#xff0c;沒有類似 Pixso 的在線 UI 設計&#xff08;如矢量繪圖、組件樣式精細化設計&#xff09;功能&#xff0c;其核心是通過預設組件快速構建數…

MySQL--組從復制的詳解及功能演練

2.MySQL的組從復制 2.1 配置mastesr [rootmysqlaa ~]# vim /etc/my.cnf [mysqld] server-id10 datadir/data/mysql socket/data/mysql/mysql.sock default_authentication_pluginmysql_native_password log-binmysql-bin[rootmysqlaa ~]# /etc/init.d/mysqld restart# 進入數據…

JavaScript將String轉為base64 筆記250802

JavaScript將String轉為base64 筆記250802 在 JavaScript 中將字符串轉換為 Base64 編碼有多種方法&#xff0c;每種方法都有其適用場景。下面我將全面介紹這些方法&#xff0c;包括處理 ASCII 字符、Unicode 字符以及性能優化方案。 基礎方法&#xff1a;btoa() 基本用法&a…

Unity3D數學第四篇:射線與碰撞檢測(交互基礎篇)

Unity3D數學第一篇&#xff1a;向量與點、線、面&#xff08;基礎篇&#xff09; Unity3D數學第二篇&#xff1a;旋轉與歐拉角、四元數&#xff08;核心變換篇&#xff09; Unity3D數學第三篇&#xff1a;坐標系與變換矩陣&#xff08;空間轉換篇&#xff09; Unity3D數學第…

數據處理和統計分析——09 數據分組

1 聚合 1.1 簡介 在SQL中我們經常使用GROUP BY將某個字段&#xff0c;按不同的取值進行分組&#xff0c;在Pandas中也有groupby()函數&#xff1b;分組之后&#xff0c;每組都會有至少1條數據&#xff0c;將這些數據進一步處理返回單個值的過程就是聚合&#xff0c;比如分組之后…

【數據結構與算法】數據結構初階:排序內容加餐(一)——快速排序:三路劃分、自省排序

&#x1f525;個人主頁&#xff1a;艾莉絲努力練劍 ?專欄傳送門&#xff1a;《C語言》、《數據結構與算法》、C語言刷題12天IO強訓、LeetCode代碼強化刷題 &#x1f349;學習方向&#xff1a;C/C方向 ??人生格言&#xff1a;為天地立心&#xff0c;為生民立命&#xff0c;為…

MySqL(加餐)

范式第一范式數據庫表的每一列都是不可分割的原子數據項&#xff0c;而不能是集合&#xff0c;數組&#xff0c;對象等非原子數據。在關系型數據庫的設計中&#xff0c;滿足第一范式是對關系模式的基本要求。不滿足第一范式的數據庫就不能被稱為關系數據庫。第一范式實際上只要…

【redis】基于工業界技術分享的內容總結

Redis 實踐指南與核心概念 一、Java 中常用的 Redis 使用場景與實踐 緩存&#xff08;Caching&#xff09; 場景&#xff1a;熱點數據、頻繁訪問的數據&#xff0c;如商品詳情、用戶信息。通過緩存減少數據庫壓力&#xff0c;提高系統響應速度。 工業界實踐&#xff1a; 淘寶…

服務端之nestJS常用異常類及封裝自定義響應模塊

MENU前言常用異常類&#xff08;由nestjs/common提供&#xff09;示例自定義異常&#xff08;可選&#xff09;自定義響應模塊前言 在NestJS中&#xff0c;nestjs/common提供了大量的內置異常類&#xff0c;主要用于在控制器、服務等層拋出特定的HTTP錯誤響應。 常用異常類&…

數據鏈路層、NAT、代理服務、內網穿透

目錄 一. 以太網 以太網幀格式 二. MAC地址 三. MTU 四. ARP協議 五. NAT NAPT 六. 代理服務器 正向代理 反向代理 七. 內網穿透 八. 內網打洞 一. 以太網 ? "以太網" 不是一種具體的網絡, 而是一種技術標準; 既包含了數據鏈路層的內 容, 也包含了一些物理層…

Rust在CentOS 6上的移植

Rust已不支持Cent OS 6 rhel是Redhat 發布的Red Hat Enterprise Linux的簡稱&#xff0c;使用rhel源代碼編譯的CentOS&#xff0c;最新的版本是CentOS 7&#xff0c;于2024年停止支持。而更古老的CentOS 6&#xff0c;則在2020年就已經結束了。 而面對如此老舊的系統&#xf…

C++音視頻開發:基礎面試題

音視頻領域技術門檻高&#xff0c;學習資料稀缺&#xff0c;體系化書籍和開發工具有限&#xff0c;新手入門困難。音視頻開發涉及眾多任務&#xff1a;音頻&#xff08;采集、編解碼、降噪等&#xff09;、視頻&#xff08;采集、編解碼、圖像處理&#xff09;、實時傳輸&#…

C++刷題 - 7.27

貪心算法的詳細邏輯這個問題的最優解可以用 貪心算法 在 O(N) 時間 內解決。它的核心思想是&#xff1a;每次操作盡可能覆蓋最長的連續非零區間&#xff0c;并通過數學分析發現&#xff1a;最小操作次數等于所有“上升臺階”的高度差之和。1. 直觀理解假設 steps [1, 2, 3, 2,…

音頻3A處理簡介之AGC(自動增益控制)

在音頻通話和視頻會議中&#xff0c;音頻自動增益控制AGC模塊的主要作用&#xff1a;? 穩定音頻信號的輸出電平。無論麥克風采集信號的強弱&#xff08;如用戶離麥克風遠近程度不同&#xff09;&#xff0c;盡可能保證音頻采集模塊的輸出音量保持相對一致&#xff0c;不會偏大…