以下是基于笔记更详细的知识梳理,从概念到细节逐层拆解,帮你吃透数据结构核心要点:

数据结构部分的重点内容:

在这里插入图片描述

一、数据结构基础框架

(一)逻辑结构(关注元素间“逻辑关系”)

笔记里提到“集合、线性、树形、图形结构”,具体含义:

  • 集合结构:元素间仅“同属一个集合”的关系,无明确关联规则(如存一批用户 ID,相互独立 )。

  • 线性结构:元素像排队,一对一顺序关联(如数组、链表,每个元素(除首尾)有唯一前驱和后继 )。

  • 在这里插入图片描述

  • 树形结构:元素是“一对多”层级关系(如公司组织架构,总经理→部门经理→员工 ),典型如二叉树(每个节点最多俩子节点 )。

  • 在这里插入图片描述

  • 图形结构:元素“多对多”关联(如社交网络好友关系,A 可连 B、C,B 也能连 C、D ),强调复杂网状连接。
    在这里插入图片描述

(二)物理结构(关注“内存怎么存” )

笔记里的顺序、链式、索引、散列,是数据在内存的存储方式,直接影响增删查改效率

  • 顺序结构(如数组)

    • 存储特点:占一整块连续内存,像火车车厢连成片。比如 int arr[5],5 个 int 依次存在内存,地址连续。
    • 访问效率:因内存连续,用下标访问(如 arr[2] ),CPU 直接算偏移量,时间复杂度 O(1)O(1)O(1)(秒查 )。
    • 增删痛点:插入/删除元素,后续元素得“搬家”。比如数组 [1,2,3,4] 要在第 2 位插 5,就得把 2、3、4 后移,数据量大时超耗时,复杂度 O(n)O(n)O(n)
    • 内存碎片隐患:若提前分配大内存(比如预开 100 长度数组,实际只用 20 ),剩下 80 可能因“不连续”被浪费(内部碎片 )。
  • 链式结构(如链表)

    • 存储特点:元素(节点)分散在内存,靠指针“牵线”。每个节点存数据 + 指向下一节点的指针(单向链表 ),双向链表还多一个指向前驱的指针。
    • 增删优势:插入/删除只需改指针。比如单向链表删节点 B,只要让 A 的指针跳过 BC;插入同理,改前后指针就行,复杂度 O(1)O(1)O(1)(定位到位置后秒改 )。
    • 查找劣势:因内存不连续,找元素得从头遍历。比如找第 10 个节点,必须从表头开始,一个个指针跳,复杂度 O(n)O(n)O(n)(数据多了超慢 )。
    • 内存利用灵活:不用预分配连续大内存,元素按需“零散”分配,能减少内部碎片,但动态分配多了可能产生外部碎片(小内存块难利用 )。
  • 索引结构(笔记里“索引表”相关)

    • 核心逻辑:额外建“索引表”,存数据关键字 + 对应存储位置(地址 )。比如查字典,索引表像“目录”,找 “数据结构” 词条,先查目录找页码,再翻对应页。
    • 适用场景:数据量大、查询频繁时,用索引加速。比如数据库查用户,用 “手机号” 做索引,不用遍历全表,直接定位存储位置,查得快。
    • 代价:维护索引表占额外内存,且增删数据时,索引表也得跟着更新,耗性能。
  • 散列结构(哈希表)

    • 核心逻辑:用 哈希函数,把数据关键字(如用户 ID )映射成内存地址。比如哈希函数 f(key)=key%10key=123 就存在地址 123%10=3 位置。
    • 查询优势:理想情况,直接算地址访问,复杂度 O(1)O(1)O(1)(和数组下标访问一样快 )。
    • 哈希冲突问题:不同关键字可能算出相同地址(比如 1222%10=2 ),得用链表法、开放寻址法解决,处理冲突会增加复杂度。

二、链表(笔记重点,掰开揉碎讲)

在这里插入图片描述

(一)链表的“家族成员”

笔记里提到单向、双向、内核链表、循环链表,逐个说:

  • 单向链表

    • 结构:节点 = 数据域(存值,如 int data ) + 指针域(存下一节点地址,如 struct node *pnext )。表头是 *phead,表尾节点 pnext=NULL(标志结束 )。
    • 操作限制:只能从表头往后遍历,想找前一个节点?没指针,得从头再来,所以反向操作超麻烦(比如删节点,得先找它前驱,单向链表只能遍历 )。
  • 双向链表

    • 结构升级:节点多一个指针域(struct node *pprev ),指向前一节点。这样,往前、往后遍历都能实现。
    • 实用场景:频繁需要“前后跳转”的场景。比如浏览器历史记录,回退(往前找 )、前进(往后找 ),双向链表更顺手。
  • 循环链表

    • 变种玩法:表尾节点的 pnext 不指向 NULL,而是指向表头,形成环。比如单向循环链表,从任意节点出发,能遍历全表;双向循环链表同理,前后指针都能绕环。
    • 典型应用:操作系统“时间片轮转”调度,多个进程用循环链表管理,轮流执行,到表尾自动回到表头。
  • 内核链表(进阶,理解设计思想)

    • 设计巧妙:不把数据直接放节点,而是让节点“嵌入”数据结构里。比如 Linux 内核链表,用 struct list_head 做通用节点,其他结构体(如进程控制块 task_struct )包含这个节点,实现“用一套链表代码管理所有数据”,高度解耦、复用性强。
(二)链表的“对象封装”(笔记里 struct link 相关 )
  • 为啥封装:直接操作节点太零散,封装成“链表对象”,方便管理。
  • struct link 里的“小心思”
    • struct node *phead:表头指针,找链表入口。
    • int clen:存节点个数,想知道链表多长,直接读 clen,不用遍历统计。
    • 操作函数配套:创建链表(init_link() )、插入节点(insert_node() )、删除节点(delete_node() )、销毁链表(destroy_link() )等,把链表当“对象”用,逻辑更清晰。

三、内存碎片(笔记里“内/外碎片” )

(一)内部碎片
  • 咋产生的:用顺序结构(如数组)或某些“规则数据类型”时,预分配的内存没被完全利用。比如 C 语言 struct 按内存对齐分配,可能多占几个字节;数组开 100 长度,只用 50,剩下 50 因“属于数组”不能被其他数据用,成了内部碎片。
(二)外部碎片
  • 核心问题:动态分配内存(如链表频繁 malloc ),释放后,小内存块“零散分布”,无法合并成大内存块。比如多次 malloc 小节点,释放后内存里有很多小空闲块,新数据要大内存时,这些小块没法用,成了外部碎片。

四、数据结构“常用操作基石”

(一)指针
  • 关键作用:链式结构的“命脉”,链表靠指针串节点;动态内存分配(malloc )返回的也是指针,管理堆内存离不开它。
(二)结构体(struct
  • 定制化容器:把不同类型数据“打包”。比如链表节点 struct node,把 int data(数据 )和 struct node *pnext(指针 )放一起,让数据 + 关联关系“一体化”。
(三)动态内存分配(malloc/free 等 )
  • 灵活双刃剑:链表节点按需 malloc,用多少开多少;但频繁分配/释放,容易内存泄漏(忘 free )、产生外部碎片,得小心管理。

五、总结(知识串联,更清晰 )

数据结构的核心是**“用啥结构存数据 + 咋高效操作数据”**:

  • 存数据前,选逻辑结构(比如一对一关系用线性结构,多对多用图形结构 )。
  • 存的时候,选物理结构(数组存连续内存,链表存零散内存,索引/散列加速查询 )。
  • 操作数据时,链表靠指针玩“增删自由”,数组靠下标玩“访问速度”,各有优劣。

笔记里的链表、内存碎片、动态分配,都是围绕“怎么高效存、改、查数据”展开,理解这些,学队列、栈、树、图时,逻辑会更顺(比如队列基于数组/链表实现,本质是线性结构的“特殊规则操作” )。
在这里插入图片描述
在这里插入图片描述

课上代码:

需要做到多练习,自己理解完单向链表之后多敲代码熟练运用:

封装函数部分:

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include"link.h"
LINK_T *creat_link()
{LINK_T *plink=malloc(sizeof(LINK_T));if(plink==NULL){printf("malloc plink error");return NULL;}plink->phead=NULL;plink->clen=0;return plink;
}//头插入:
int insert_node(LINK_T *plink,node_type data)
{NODE_T *pnode=malloc(sizeof(NODE_T));if(pnode==NULL){printf("malloc pnode error");return -1;}pnode->data=data;pnode->pnext=plink->phead;plink->phead=pnode;plink->clen++;return 0;
}
//遍历:
void link_for_each(LINK_T *plink)
{NODE_T *p=plink->phead;while(p!=NULL){printf("%d ", p->data );p=p->pnext;}printf("\n");
}
//遍历查找结点数据:
NODE_T *find_link(LINK_T *plink,node_type data)
{NODE_T *p=plink->phead;while(p!=NULL){if(p->data==data){printf("found!");return p;}p=p->pnext;}return NULL;
}
//修改结点数据:
int change_link(LINK_T *plink,node_type olddata,node_type newdata)
{NODE_T *p=find_link(plink,olddata);if(p==NULL){printf("nofound!");return -1;}p->data=newdata;return 0;
}//头删:
void delet_firstNode(LINK_T *plink)
{NODE_T *p=plink->phead;plink->phead=p->pnext;free(p);p=NULL;plink->clen--;
}
//指定数据对应的结点进行删除:
int delet_specified_node(LINK_T *plink,node_type data)
{NODE_T *p=find_link(plink,data);if(p==NULL){printf("nofound!");return -1;}NODE_T *p_prehand=plink->phead;if(p_prehand==NULL){printf("无结点,无法继续删除结点");return -1;}while(p_prehand!=NULL){if(p_prehand->pnext==p){p_prehand->pnext=p->pnext;break;}p_prehand=p_prehand->pnext;}free(p);p=NULL;plink->clen--;return 0;
}
//封装函数实现单向链表的尾插:
NODE_T *findTheLastNode(LINK_T *plink)
{NODE_T *p=plink->phead;if(p==NULL){return NULL;}while(p->pnext!=NULL){p=p->pnext;}return p;
}
int insertOneNodeAtTheEndOfTheLinkedList(LINK_T *plink,node_type data)
{NODE_T *pnode=malloc(sizeof(NODE_T));if(pnode==NULL){printf("malloc error!");return -1;}NODE_T *plastnode=findTheLastNode(plink);if(plastnode==NULL){plink->phead=pnode;plink->clen++;pnode->data=data;pnode->pnext=NULL;}else{plastnode->pnext=pnode;pnode->pnext=NULL;pnode->data=data;plink->clen++;}return 0;
}
//封装函数实现单向链表的尾删,注意只有一个结点的情况!
int delet_lastnode(LINK_T *plink)
{NODE_T *p=plink->phead;if(p==NULL){printf("无结点可删除,程序终止!");return -1;}else{if(p->pnext==NULL){free(p);plink->phead=NULL;plink->clen--;return 0;}else{while(p->pnext->pnext!=NULL){p=p->pnext;}free(p->pnext);p->pnext=NULL;plink->clen--;}}return 0;
}
int deletAllNode(LINK_T *plink)
{NODE_T *p=plink->phead;if(p==NULL){printf("无结点可删除");return -1;}while(plink->phead!=NULL){while(p!=NULL){p=p->pnext;}delet_lastnode(plink);}
}

头文件部分:

#ifndef _LINK_H_
#define _LINK_H_
typedef int node_type;
typedef struct node
{node_type data;struct node *pnext;
}NODE_T;
typedef struct link
{NODE_T *phead;int clen;
}LINK_T;extern LINK_T *creat_link();
extern int insert_node(LINK_T *plink,node_type data);
extern void link_for_each(LINK_T *plink);
extern NODE_T *find_link(LINK_T *plink,node_type data);
extern int change_link(LINK_T *plink,node_type olddata,node_type newdata);
extern void delet_firstNode(LINK_T *plink);
extern int delet_specified_node(LINK_T *plink,node_type data);
extern NODE_T *findTheLastNode(LINK_T *plink);
extern int insertOneNodeAtTheEndOfTheLinkedList(LINK_T *plink,node_type data);
extern int delet_lastnode(LINK_T *plink);
extern int deletAllNode(LINK_T *plink);
#endif

主函数本部分:

#include<stdio.h>
#include"link.h"
#include<stdlib.h>
int main(void)
{link_t *plink=creat_Link();if(plink==NULL){return -1;}insert_link_head(plink,1);insert_link_head(plink,2);insert_link_head(plink,3);insert_link_head(plink,4);insert_link_head(plink,5);link_for_each(plink);
//	Node_t *pfind=find_link(plink,2);
//	if(pfind==NULL)
//	{
//		printf("nofind");
//	}
//	else
//	{
//		printf("found,value=%d\n",pfind->data);
//	}
//	change_link(plink,2,3);
//	link_for_each(plink);deletFirstNode(plink);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/94217.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/94217.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/94217.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

模型学习系列之参数

背景 “GLM-4.5拥有 3550 亿总参数量&#xff0c;其中 320 亿活跃参数&#xff1b;GLM-4.5-Air 采用更紧凑的设计&#xff0c;拥有 1060 亿总参数量&#xff0c;其中 120 亿活跃参数。” 定义与关系 总参数量&#xff1a;模型中所有可训练参数的总和&#xff08;包括嵌入层、注…

[创业之路-535]:软件需要原型验证、产品需要原型验证、商业模式也需要原型验证

原型验证在软件、产品开发以及商业模式探索中均扮演着至关重要的角色&#xff0c;它通过低成本、快速迭代的方式&#xff0c;帮助团队验证核心假设、降低风险并优化方案。以下是针对这三个领域的具体分析&#xff1a;一、软件原型验证&#xff1a;从概念到可交互的模型核心目的…

sublime text2配置

sublime text2配置背景配置其他背景 之前下载了就把它当记事本在使用。但是&#xff0c;在使用过程中&#xff0c;有些场景很痛苦。如果说找一个字符串中的某一部分&#xff0c;虽然它通过了这个功能&#xff0c;但是不够明显&#xff0c;看瞎了。。。 配置 下面是我改的一些选…

本地通信的选择:为什么组播比广播更适合多进程协作?

零、深入解析Linux本地通信机制,对比广播与组播的核心差异 本地组播能让多进程收到消息,而本地广播不行,核心原因在于两者的设计目标、网络协议处理逻辑以及内核转发机制存在本质差异。具体可以从以下几个角度理解: 1. 通信模式与目标地址的本质区别 组播(Multicast):…

7-Django项目实战[user]-发送邮件激活账号

1.前期准备&#xff08;以QQ邮箱为例&#xff09; 登录QQ邮箱 获取授权码 2.settings.py文件配置 1&#xff09;缓存配置 # 配置缓存 CACHES {# 邮件激活随机数"default": {"BACKEND": "django_redis.cache.RedisCache","LOCATION&q…

社群团购市场选择与开源技术赋能下的下沉市场开拓策略研究——以开源AI智能名片、链动2+1模式与S2B2C商城小程序为例

摘要&#xff1a;在社群团购行业面临流量成本攀升与同质化竞争的背景下&#xff0c;下沉市场因其庞大用户基数与未被充分满足的消费需求&#xff0c;成为创业者突破增长瓶颈的关键赛道。本文以拼多多成功开拓小城镇与农村市场的案例为切入点&#xff0c;结合开源AI智能名片、链…

Ollama前端:open-webui

github&#xff1a;https://github.com/open-webui/open-webui 官网&#xff1a;&#x1f3e1; Home | Open WebUI 1、docker安装&#xff08;GPU&#xff09;&#xff1a; docker run -d -p 3000:8080 --gpusall -v ollama:/root/.ollama -v open-webui:/app/backend/data …

LeetCode513:找树最左下角的值(bfs+dfs)

文章目录一、 题目描述解法一&#xff1a;层序遍历 (BFS) - 最直观的解法核心思路代码实现优缺点分析解法二&#xff1a;递归 (DFS) - 更深度的思考核心思路代码实现优缺点分析四、 总结与对比LeetCode 513 - 寻找树的最后一行的最左侧的值&#xff0c;【难度&#xff1a;中等&…

把“评论”菜单从WordPress后台移除的3种方法

在WordPress后台移除“评论”菜单&#xff0c;可以通过以下几种方法实现。以下是详细步骤&#xff1a; 方法1&#xff1a;通过代码移除(推荐) 将以下代码添加到主题的functions.php文件中(或使用CodeSnippets插件)&#xff1a; // 移除后台左侧菜单的“评论” add_action(ad…

大语言模型 LLM 通过 Excel 知识库 增强日志分析,根因分析能力的技术方案(4):只要过一遍LLM的简约版本

文章大纲 只要过一遍LLM的简约版本 1 设计原理(一句话) 2 极简数据流 3 最小依赖实现(本地 SQLite + OpenAI 兼容端点) 3.1 一次性准备:Excel → SQLite 3.2 关键词提取 + 查表(正则 / SQL) 3.3 单次 LLM 调用 4 运行结果示例 5 性能 & Token 对比 6 可扩展点 7 参考…

(转)mybatis和hibernate的 缓存区别?

MyBatis 和 Hibernate 都是流行的 Java 持久化框架&#xff0c;它们都提供了自己的缓存机制来优化数据库操作&#xff0c;减少数据库的访问次数&#xff0c;提高应用程序的性能。尽管两者都支持缓存&#xff0c;但是它们的缓存实现方式和配置有所不同。1. 缓存机制的基本区别My…

【linux内核系列】:万字详解进程间通信:消息队列

&#x1f525; 本文专栏&#xff1a;Linux &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 你讨厌的现在&#xff0c;是未来的你拼命想回去修正的战场。 ★★★ 本文前置知识&#xff1a; 匿名管道 命名管道 共享内存 前…

React 19 革命性升级:编译器自动优化,告别手动性能调优时代

概述 React 19 是 React 框架的一个重要里程碑版本&#xff0c;带来了众多突破性的改进和新特性。本文档将详细介绍 React 19 的主要变化&#xff0c;帮助开发者了解并迁移到新版本。 &#x1f680; 主要新特性 React Compiler (编译器) React 19 引入了全新的 React Compi…

UE5的渲染Debug技巧

ShaderPrint UE5相对UE4使用的ComputeShader(GPU Driven)的地方多很多。因为UE5为了方便查看ComputeShader的某些值&#xff0c;开发了“ShaderPrint”&#xff0c;方便直接在Shader 打印信息到屏幕&#xff0c;而不用采用CPUReadback在print的方式。 比如r.nanite.ShowStats…

【2025/08/03】GitHub 今日热门项目

GitHub 今日热门项目 &#x1f680; 每日精选优质开源项目 | 发现优质开源项目&#xff0c;跟上技术发展趋势 &#x1f4cb; 报告概览 &#x1f4ca; 统计项&#x1f4c8; 数值&#x1f4dd; 说明&#x1f4c5; 报告日期2025-08-03 (周日)GitHub Trending 每日快照&#x1f55…

Android系统模块编译调试与Ninja使用指南

模块编译调试方法 (此处举例framework、installd、SystemUI等模块的编译调试&#xff0c;其他类似) 1. Framework模块编译 Android系统代码的framework目录内&#xff0c;一共有3个模块单独编译&#xff1a;framework、services、framework-res.apk。 注意&#xff1a;偶尔会有…

【硬件-笔试面试题】硬件/电子工程师,笔试面试题-51,(知识点:stm32,GPIO基础知识)

目录 1、题目 2、解答 3、相关知识点 一、GPIO 基本结构与特性 1. GPIO 硬件结构 2. 主要特性 二、GPIO 工作模式 1. 输入模式 2. 输出模式 3. 复用功能模式 4. 特殊模式 三、GPIO 配置步骤&#xff08;以 STM32Cube HAL 库为例&#xff09; 1. 初始化 GPIO 时钟 …

小智服务器Java安装编译(xinnan-tech)版

github&#xff1a;https://github.com/xinnan-tech/xiaozhi-esp32-server 一、JDK 1、JDK21下载&#xff1a; https://www.oracle.com/cn/java/technologies/downloads/#jdk21-windows RPM安装&#xff1a; rpm -ivh jdk-21_linux-x64_bin.rpm 2、IDEA设置JDK File → P…

智能平台的感知进化:AI × 视频通感在群体终端协同中的应用探索

✳️ 引言&#xff1a;从单兵到集群&#xff0c;未来智能平台的协同演进 从传统的单兵执行任务到如今的“群体智能平台编组”&#xff0c;现代感知系统正经历一场由 AI、机器人与智能计算平台驱动的深度变革。过去&#xff0c;履带式无人平台在平坦地形中承担支援任务&#xf…

基于定制开发开源AI智能名片S2B2C商城小程序的B站私域流量引流策略研究

摘要&#xff1a;随着移动互联网进入存量竞争阶段&#xff0c;私域流量运营成为企业数字化转型的核心战略。B站作为中国最大的Z世代文化社区&#xff0c;其3.41亿月活跃用户中Z世代占比达58%&#xff0c;且25岁以上用户增速显著&#xff0c;用户日均使用时长超108分钟&#xff…