版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
数据结构概论Chapter1:IntroductiontoDataStructures计算机科学基础核心课程,探索数据的组织、存储与处理逻辑,
是构建高效算法、操作系统与数据库的底层基石。核心通识课·必修学分课程教学大纲一、课程基本信息:总学时64学时(理论48学时+实践16学时)二、教学内容与学时分配章节教学内容理论学时实践学时第1章:数据结构概论基本概念、ADT、算法分析41第2章:线性表顺序表、链表、应用62第3章:栈定义、实现、应用41第4章:队列定义、实现、应用41第5章:串存储、KMP算法41第6章:数组与广义表数组存储、稀疏矩阵41第7章:树和二叉树二叉树、遍历、哈夫曼树83第8章:图存储、遍历、最小生成树、最短路径63第9章:查找静态/动态查找、哈希表41第10章:排序内部排序算法42总计4816三、实践教学内容与要求(16学时)1.基础验证性实验(12学时):实验一(线性表,2学时)、实验二(栈与队列,1学时)、实验三(串,1学时)、实验四(数组与广义表,1学时)、实验五(二叉树,3学时)、实验六(图,3学时)、实验七(查找与排序,1学时)。2.综合设计性实验(4学时):课程设计,综合运用数据结构知识解决一个小型实际问题。课程思政:数据结构中的中国智慧与社会责任“几何定理机器证明的开创性算法,不仅是计算机科学的技术突破,更是中国智慧在基础学科领域的生动诠释与创新典范。”彰显智慧与创新以吴文俊院士等前辈为榜样,感悟中国学者在算法领域的原创性贡献。这不仅是技术的传承,更是激发民族自豪感、树立文化自信与创新勇气的精神源泉。坚守责任与严谨数据结构的设计关乎系统安全与稳定,一个微小的缺陷可能引发隐私泄露等风险。这要求我们在学习中打磨严谨的工程态度,始终铭记技术工作者的社会责任。锻造思维与协作学习数据结构是训练抽象与逻辑思维的过程。面对复杂的大型数据工程,唯有依靠高效的团队协作与沟通互助,才能攻克难题,践行集体主义精神。什么是数据结构?从数值计算到非数值计算01早期:纯粹的数值计算时代计算机仅用于解决数学方程、工程运算等问题,处理的是整型、实型等基础数据。核心关注点在于算法逻辑与数学公式的优化,数据本身结构简单,无需复杂的组织方式。02现代:复杂的非数值计算需求应用渗透至电商、社交、金融等领域,需处理字符串、图像、多维记录等复杂数据。此时,如何设计高效的数据结构来组织、存储和操作数据,成为决定系统性能的关键。核心洞察:数据结构是连接“原始数据”与“高效算法”的桥梁,从简单的变量到复杂的数据库,其设计直接决定了程序的效率与扩展性。典型场景:学生信息管理系统
这是典型的非数值计算案例,系统需要对包含学号、姓名、成绩等多字段的记录进行高频的增删改查。合理的数据结构设计,是保障这些操作快速、稳定运行的基石。基本概念和术语01数据(Data)定义:所有能输入计算机并被其处理的符号总称,是计算机加工处理的“原料”。分类:数值数据(整数、实数)与非数值数据(字符、图像、音频等)。02数据元素(DataElement)定义:数据的基本单位,在程序中通常作为一个整体被独立考虑和处理。构成:由若干不可分割的数据项(字段)组成,例如学生信息表中的一条记录。03数据对象(DataObject)定义:性质相同的数据元素的集合,是数据的一个子集,具有共同的特征。示例:全体学生的基本信息记录集合、26个英文字母组成的字符集合。04数据结构(DataStructure)定义:相互之间存在一种或多种特定关系的数据元素的集合,研究对象间的逻辑与物理关系。形式化描述:Data_Structure=(D,R),D是数据元素集,R是关系集。四类基本逻辑结构逻辑结构是数据元素之间抽象的相互关系,它不依赖于数据的存储位置,而是决定了数据的组织方式与处理效率,是构建复杂算法的基石。01集合结构核心关系:元素同属一个集合,无特定顺序,关系最为松散。
典型示例:一个班级的全体学生、图书馆的藏书合集。02线性结构核心关系:元素间呈一对一的有序排列,形成线性序列。
典型示例:排队的人群、学生信息表、购物清单。03树形结构核心关系:元素间呈一对多的层次关系,具有明显的分支和层级。
典型示例:公司组织架构、电脑文件目录、家谱图。04图形结构核心关系:元素间呈多对多的任意关联,构成复杂的网状结构。
典型示例:城市交通路网、社交关系网、课程选修依赖图。逻辑结构vs.物理结构01数据的逻辑结构是对数据元素之间逻辑关系的抽象描述,剥离了具体的存储细节,专注于构建解决问题的数学模型,是算法设计的理论基石。集合结构线性结构树形结构图形结构核心视角:聚焦“数据元素间的关系本质”,独立于具体的计算机硬件环境,是面向问题的抽象层面。02数据的物理结构(存储结构)顺序存储:逻辑相邻则物理地址连续(如数组)。特点是支持随机访问,存取速度快,但插入删除时需移动大量元素,空间利用率易受限于连续内存。链式存储:逻辑相邻物理可离散,通过指针关联(如链表)。特点是插入删除灵活,无需连续内存,但无法随机访问,且需额外空间存储指针。二者的辩证关系:算法设计与实现的桥梁逻辑结构决定了“如何组织数据”(算法的设计蓝图),物理结构决定了“如何高效存取”(算法的执行效率)。脱离逻辑的物理实现是无本之木,脱离物理的逻辑设计则是空中楼阁,二者紧密耦合,共同决定了数据处理的性能上限。数据结构课程的内容和任务唐纳德·克努特(DonaldKnuth),算法与程序设计领域的泰斗,《计算机程序设计艺术》的作者,他的著作系统地奠定了数据结构与算法分析的理论基础。01抽象建模任务:剥离问题表象,分析数据元素间的逻辑关系,提炼核心结构。目标:将现实问题转化为计算机可处理的逻辑模型,明确基本运算规则。02物理实现任务:选择数组、链表等存储方式,编写代码实现逻辑结构与算法。目标:结合硬件特性优化存储效率,确保设计方案的工程可行性。03性能评价任务:分析时间与空间复杂度,对比不同方案的效率与资源消耗。目标:根据应用场景需求,在多种方案中做出最优技术抉择。💡核心思维:数据结构不仅是代码的组织形式,更是解决复杂计算问题的思维框架,是构建高效软件系统的底层逻辑。数据类型与抽象数据类型(ADT)01数据类型(DataType)定义:一组具有相同性质的值的集合,以及定义在该集合上的一组操作的总称。原子类型:不可再分的基本类型(如int,char,float),是构建复杂数据的基石。结构类型:由多个成分按特定结构组成(如数组、结构体),用于描述复杂实体。02抽象数据类型(ADT)本质定义:一个数学模型以及定义在该模型上的一组操作,是对数据类型的抽象化描述。核心特征:只关注数据的逻辑特性和外部可用操作,与具体的存储结构和实现算法无关。设计哲学:通过封装隐藏实现细节,仅暴露接口,实现“黑盒”式的数据操作。📐形式化三元组表示ADT=(D,S,P)D:数据对象集合|S:D上的关系集P:对D的基本操作集(核心接口)🚀工程核心优势•提升软件模块复用性与维护性•降低系统设计复杂度,解耦逻辑•有效隔离错误,增强系统稳定性🎯抽象思维价值•从“怎么做”转向“做什么”的思维跃迁•是面向对象编程(OOP)的理论基础•实现数据与操作的完美封装与整合参数传递:传值调用vs.传址调用01传值调用(CallbyValue)机制:将实参的值复制给形参,二者拥有独立的内存空间,互不干扰。特点:函数内部对形参的修改仅作用于副本,不会影响外部实参。场景:适用于仅需读取数据、不希望改变原始值的计算场景。02传址调用(CallbyReference)机制:传递实参的内存地址,形参作为指针指向实参的存储位置。特点:对形参的修改会直接作用于实参,实现数据的双向传递。场景:用于需要修改原始数据、交换变量值或传递大型数据结构。💻核心差异代码对比//传值调用:仅交换函数内部的局部变量
voidswap1(intx,inty){
inttemp=x;x=y;y=temp;//外部实参a,b不会改变
}//传址调用:直接操作内存地址,修改实参
voidswap2(int*x,int*y){
inttemp=*x;*x=*y;*y=temp;//外部实参a,b完成交换
}算法与算法分析01算法的核心定义与特性定义:对特定问题求解步骤的精准描述,是指令的有限序列。需注意:程序不一定满足有穷性(如操作系统),而算法必须严格具备。有穷性执行有限步后终止,不能无限循环确定性每一步指令含义明确,无二义性可行性操作可通过基本运算在有限时间内完成输入(Input)有零个或多个输入,取自特定的数据对象集合输出(Output)有一个或多个输出,是与输入有特定关系的量02衡量好算法的四大维度正确性Correctness满足预先规定的功能和性能要求,能正确处理典型输入、边界值及各种异常情况。可读性Readability算法思路清晰、逻辑结构规范,便于理解、交流、调试和后期维护。健壮性Robustness对非法的输入数据能做出正确的反应或适当处理,防止程序崩溃或产生错误结果。高效性Efficiency运行时间短(时间复杂度低)且占用存储空间少(空间复杂度低),兼顾时间与空间成本。算法性能分析:时间与空间复杂度01时间复杂度TimeComplexity定义:量化算法执行时间随数据规模(n)增长的趋势,反映程序运行效率的快慢。表示:采用大O表示法描述增长量级,如常数阶O(1)、线性阶O(n)等。核心:统计基本操作的执行次数,忽略低阶项与常数,取增长最快的项。02空间复杂度SpaceComplexity定义:衡量算法运行过程中所需消耗的额外存储空间资源。表示:沿用大O表示法,重点评估输入数据之外的临时变量与辅助空间。核心:关注算法运行时的内存占用增长,包括局部变量、动态数组及递归栈。工程实践:时空权衡与场景化决策本质博弈:算法设计往往需要在时间与空间之间做取舍。例如哈希表利用“额外空间”换取近乎常数级的查询速度;而数据压缩算法则通过增加计算耗时来大幅节省存储开销。决策依据:高并发接口优先优化时间复杂度以保障响应速度;嵌入式、移动端等内存受限场景,则需优先控制空间复杂度,避免内存溢出,追求场景下的相对最优解。时间复杂度详解:大O表示法图示直观展示了不同复杂度随数据规模增长的趋势差异,指数级增长最为陡峭,应尽量避免。大O表示法描述了算法执行时间的理论上限,它反映了算法运行时间随数据规模n增长的趋势,而非具体的执行时间,是衡量算法效率的核心指标。▍复杂度增长阶梯(由优至劣排序)O(1)<O(logn)<O(n)<O(nlogn)<O(n²)<O(2ⁿ)<O(n!)O(1)常量阶执行时间与规模无关,速度最快。典型如直接访问数组下标。O(n)线性阶时间随规模线性增长。典型如单层循环、顺序查找算法。O(n²)平方阶增长显著,性能消耗大。常见于两层嵌套循环,数据量大时慎用。分析三步走:1.找出执行次数最多的基本操作;2.推导出执行次数关于n的函数f(n);3.仅保留最高次项并忽略系数,即得到大O表示。数据结构的应用领域计算机科学的骨架数据结构是连接算法与程序的桥梁,它不仅决定了系统的运行效率,更是构建复杂软件系统的底层逻辑基础,渗透在从系统内核到上层应用的每一个角落。系统软件基石•操作系统:利用队列调度进程,链表管理内存碎片,确保系统高效运转。•数据库:B+树与哈希表是实现千万级数据毫秒级检索的核心。上层应用引擎•搜索与AI:倒排索引支撑搜索引擎,图结构构建复杂的知识图谱与神经网络。•金融分析:利用树模型进行风险评估与量化交易策略构建。前沿交叉探索•生物信息:利用多维数组与后缀树分析海量基因序列数据。•推荐系统:基于图算法的协同过滤,实现个性化内容精准推送。数据结构学习方法01夯实理论基础研读《算法导论》等经典教材,结合Coursera等优质网课系统学习,梳理各类结构的特性与复杂度,构建扎实的知识体系。02强化代码实践用C或Python手写实现核心结构,在LeetCode等平台攻克经典习题,并尝试在小型项目中落地应用,以练促学,知行合一。03深析经典案例拆解Linux内核、Redis等开源项目中的应用,探究搜索引擎、推荐系统背后的算法逻辑,理解工业级场景的设计思想。04积极交流碰撞加入学习小组分享解题思路,活跃于StackOverflow、CSDN等技术社区,在提问与解答中拓宽思维边界,及时发现并填补知识盲区。05善用可视模拟借助VisuAlgo等工具动态演示执行过程,在纸上手动模拟算法步骤,将抽象的逻辑转化为直观的视觉呈现与具象的推演过程。数据结构在人工智能中的应用01数据预处理阶段利用数组与链表高效存储海量原始数据,通过哈希表实现快速检索、去重与特征映射,为模型训练筑牢高质量数据根基。02模型构建与训练以树形结构搭建决策树与随机森林模型;用图结构构建知识图谱挖掘实体关联;通过优化底层数据结构,显著提升梯度下降等核心算法的训练效率。03模型推理与应用在智能推荐中,利用图算法解析用户-物品复杂关系网;在NLP领域,通过树形结构解析句法依存关系,精准捕捉文本深层语义,赋能下游应用。神经网络拓扑结构
层间连接与权重更新依赖高效数据结构支撑知识图谱图结构应用
图结构助力挖掘实体间的潜在关联与复杂逻辑本章小结01核心概念回顾数据结构本质:研究数据的逻辑组织、物理存储方式及相关操作算法的学科基础。两大结构:逻辑结构(线性、树形、图形)决定关系,存储结构(顺序、链式)决定实现。02算法性能分析五大基本特性:具备有穷性、确定性、可行性,拥有明确的输入与输出,是解决问题的清晰指令集。复杂度评价:通过时间复杂度(大O表示法)衡量效率,空间复杂度衡量资源消耗,二者是算法优劣的核心指标。03抽象与工程实践ADT思想:抽象数据类型封装了数据表示和操作实现,有效降低耦合度,是构建模块化、可复用软件的基石。课程核心任务:将现实问题抽象为逻辑结构,择优选择存储结构实现,并利用复杂度分析科学评价方案。第2章线性表Chapter2:LinearLists(UltimateVersion)从顺序存储到链式存储,全面剖析线性结构的底层原理与应用场景,夯实数据结构的核心基础。本章学习路径01线性表基础夯实理论基石·核心概念总览(P1-P10)解析线性表的逻辑结构与ADT定义,明确本章学习的重点难点,建立结构化的思维框架与学科素养认知。02顺序表深度解析底层实现机制·核心操作全解(P11-P45)深入探索插入、删除与查找的算法逻辑,结合图示与代码逐行解析,剖析时间空间复杂度,并实战合并有序表等经典应用。03链表深度解析动态存储结构·复杂指针操作(P46-P85)系统掌握单链表、循环链表、双向链表及静态链表的实现细节,攻克指针操作难点,通过实战练习巩固增删查改核心技能。04总结与前沿应用综合对比复盘·AI场景中的数据结构(P86+)全方位对比顺序表与链表的性能差异及适用场景,探讨线性表在人工智能数据预处理中的应用,并对本章知识进行系统总结与回顾。
若结构是非空有限集,则有且仅有一个开始结点和一个终端结点,并且所有结点都最多只有一个直接前趋和一个直接后继。可表示为:(a1,a2,……,an)线性结构的定义:线性结构的特点:①只有一个首结点和尾结点;②除首尾结点外,其他结点只有一个直接前驱和一个直接后继。线性结构包括线性表、堆栈、队列、字符串、数组等等,其中,最典型、最常用的是简言之,线性结构反映结点间的逻辑关系是
一对一的(a1,a2,…ai-1,ai,ai+1
,…,an)线性表的定义:用数据元素的有限序列表示n=0时称为数据元素线性起点ai的直接前趋ai的直接后继空表线性终点线性表的定义和特点下标,是元素的序号,表示元素在表中的位置n为元素总个数,即表长分析26个英文字母组成的英文表(A,B,C,D,……,Z)例2分析学生情况登记表数据元素都是记录;元素间关系是线性数据元素都是字母;元素间关系是线性同一线性表中的元素必定具有相同特性学号姓名性别年龄班级041810205于春梅女1804级计算机1班041810260何仕鹏男2004级计算机2班041810284王爽女1904级计算机3班041810360王亚武男1804级计算机4班:::
::线性表中数据元素的类型可以为简单类型,也可以为复杂类型。许多实际应用问题所涉的基本操作有很大相似性,不应为每个具体应用单独编写一个程序。从具体应用中抽象出共性的逻辑结构和基本操作(抽象数据类型),然后实现其存储结构和基本操作。总结030102线性表的重要基本操作线性表的类型定义基本操作初始化查找插入取值删除15432线性表的顺序表示又称为顺序存储结构或顺序映像。线性表的顺序表示和实现0102顺序存储定义把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构。逻辑相邻,物理相邻顺序存储方法用一组地址连续的存储单元依次存储线性表的元素,可通过数组V[n]来实现。元素n……..元素i……..元素2元素1LoLo+mLo+(i-1)*mLo+(n-1)*m存储地址存储内容Loc(元素i)=Lo+(i-1)*m线性表的顺序表示和实现顺序存储#defineMAXSIZE100//最大长度typedefstruct{ ElemType*elem;//指向数据元素的基地址
intlength;//线性表的当前长度
}SqList;顺序表的类型定义#defineMAXSIZE10000 //图书表可能达到的最大长度typedefstruct //图书信息定义{charno[20]; //图书ISBNcharname[50]; //图书名字floatprice; //图书价格}Book;typedefstruct{Book*elem; //存储空间的基地址intlength; //图书表中当前图书个数}SqList; //图书表的顺序存储结构类型为SqList图书表的顺序存储结构类型定义补充:C语言的动态分配函数(<stdlib.h>)malloc(m)开辟m字节长度的地址空间,并返回这段空间的首地址sizeof(x)计算变量x的长度。free(p)释放指针p所指变量的存储空间,即彻底删除一个变量new类型名T(初值列表)功能:申请用于存放T类型对象的内存空间,并依初值列表赋以初值结果值:成功:T类型的指针,指向新分配的内存失败:0(NULL)int*p1=newint;
或int*p1=newint(10);delete指针p功能:
释放指针P所指向的内存。p必须是new操作的返回值deletep1;补充:C++的动态存储分配函数调用时传送给形参表的实参必须与形参在类型、个数、顺序上保持一致补充:C++中的参数传递参数传递有两种方式传值方式(参数为整型、实型、字符型等)传地址参数为指针变量参数为引用类型参数为数组名voidmain(){ floata,b; cin>>a>>b; swap(a,b); cout<<a<<endl<<b<<endl;}#include<iostream.h>voidswap(floatm,floatn){ floattemp; temp=m; m=n; n=temp;}传值方式把实参的值传送给函数局部工作区相应的副本中,函数使用这个副本执行必要的功能。函数修改的是副本的值,实参的值不变传地址方式--指针变量作参数voidmain(){ floata,b,*p1,*p2; cin>>a>>b; p1=&a;p2=&b; swap(p1,p2); cout<<a<<endl<<b<<endl;}#include<iostream.h>voidswap(float*m,float*n){ floatt; t=*m; *m=*n; *n=t;}形参变化影响实参传地址方式--指针变量作参数voidmain(){ floata,b,*p1,*p2; cin>>a>>b;
p1=&a;p2=&b; swap(p1,p2); cout<<a<<endl<<b<<endl;}#include<iostream.h>voidswap(float*m,float*n){ float*t; t=m; m=n; n=t;}形参变化不影响实参??传地址方式--引用类型作参数引用:它用来给一个对象提供一个替代的名字。#include<iostream.h>voidmain(){ inti=5; int&j=i; i=7; cout<<"i="<<i<<"j="<<j;}什么是引用???j是一个引用类型,代表i的一个替代名。i值改变时,j值也跟着改变,所以会输出。i=7j=7#include<iostream.h>voidswap(float&m,float&n){ floattemp; temp=m; m=n; n=temp;}传地址方式--引用类型作参数voidmain(){floata,b;cin>>a>>b;swap(a,b);cout<<a<<endl<<b<<endl;}传递引用给函数与传递指针的效果是一样的,形参变化实参也发生变化。引用类型作形参,在内存中并没有产生实参的副本,它直接对实参操作;而一般变量作参数,形参与实参就占用不同的存储单元,所以形参变量的值是实参变量的副本。因此,当参数传递的数据量较大时,用引用比用一般变量传递参数的时间和空间效率都好。引用类型作形参的三点说明指针参数虽然也能达到与使用引用的效果,但在被调函数中需要重复使用“*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。123传地址方式--数组名作参数#include<iostream.h>voidsub(char);voidmain(void){chara[10]=“hello”;sub(a);cout<<a<<endl;}voidsub(charb[]){b[]=“world”;}传递的是数组的首地址对形参数组所做的任何改变都将反映到实参数组中#include<iostream.h>#defineN10intmax(inta[]);voidmain(){ inta[10]; inti,m; for(i=0;i<N;i++) cin>>a[i]; m=max(a); cout<<"themaxnumberis:"<<m;}用数组作函数的参数,求10个整数的最大数intmax(intb[]){inti,n;n=b[0];for(i=1;i<N;i++) if(n<b[i])n=b[i];returnn;}练习#include<iostream.h>#defineN10voidsub(intb[]){ inti,j,temp,m; m=N/2; for(i=0;i<m;i++){j=N-1-i;temp=b[i]; b[i]=b[j];b[j]=temp; } return;}voidmain(){ inta[10],i; for(i=0;i<N;i++) cin>>a[i]; sub(a); for(i=0;i<N;i++) cout<<a[i];}用数组作为函数的参数,将数组中n个整数按相反的顺序存放,要求输入和输出在主函数中完成线性表的重要基本操作基本操作初始化查找插入取值删除15432初始化线性表L(参数用引用)StatusInitList_Sq(SqList&L)
//构造一个空的顺序表L{L.elem=newElemType[MAXSIZE];//为顺序表分配空间
if(!L.elem)exit(OVERFLOW);//存储分配失败
L.length=0; //空表长度为0returnOK;}StatusInitList_Sq(SqList*L)//构造一个空的顺序表L{ L->elem=newElemType[MAXSIZE];//为顺序表分配空间
if(!L->elem)exit(OVERFLOW);//存储分配失败
L->length=0; //空表长度为0returnOK;}初始化线性表L(参数用指针)voidDestroyList(SqList&L){if(L.elem)delete[]L.elem;//释放存储空间}voidClearList(SqList&L){L.length=0;//将线性表的长度置为0}补充:几个简单基本操作的算法实现
销毁线性表L清空线性表L补充:几个简单基本操作的算法实现intGetLength(SqListL){return(L.length);}intIsEmpty(SqListL){if(L.length==0)return1;elsereturn0;}求线性表L的长度判断线性表L是否为空线性表的重要基本操作基本操作初始化查找插入取值删除15432intGetElem(SqListL,inti,ElemType&e){if(i<1||i>L.length)returnERROR;//判断i值是否合理,若不合理,返回ERROR
e=L.elem[i-1];//第i-1的单元存储着第i个数据
returnOK;}取值(根据位置i获取相应位置数据元素的内容)随机存取获取线性表L中的某个数据元素的内容查找(根据指定数据获取数据所在的位置)顺序查找图示253457164809012345data查找
16i253457164809i253457164809i253457164809i查找成功253457164801234data查找50i2534571648i2534571648i2534571648i2534571648i查找失败查找(根据指定数据获取数据所在的位置)查找算法时间效率分析???在线性表L中查找值为e的数据元素intLocateELem(SqListL,ElemTypee){ for(i=0;i<L.length;i++) if(L.elem[i]==e)returni+1; return0;}查找(根据指定数据获取数据所在的位置)2512478936141234567892512479989361499插入2512478936142512478936142512478936140+1+2+。。。n插第4个结点之前,移动6-4+1
次插在第i个结点之前,移动n-i+1
次插入(插在第i个结点之前)判断插入位置i是否合法。【算法步骤】0102030405判断顺序表的存储空间是否已满。将第n至第i位的元素依次向后移动一个位置,空出第i个位置。将要插入的新元素e放入第i个位置。表长加1,插入成功返回OK。StatusListInsert_Sq(SqList&L,inti,ElemTypee){if(i<1||i>L.length+1)returnERROR; //i值不合法
if(L.length==MAXSIZE)returnERROR;//当前存储空间已满
for(j=L.length-1;j>=i-1;j--)L.elem[j+1]=L.elem[j];//插入位置及之后的元素后移L.elem[i-1]=e;//将新元素e放入第i个位置++L.length; //表长增1returnOK;}【算法描述】在线性表L中第i个数据元素之前插入数据元素e若插入在尾结点之后,则根本无需移动(特别快);若元素全部后移(特别慢);若要考虑在各种位置插入(共n+1种可能)的平均移动次数,该如何计算?ACN算法时间主要耗费在移动元素的操作上【算法分析】251247893614123456789251247361425124736142512473614删除删除(删除第i个结点)0+1+2+…n-1删除第4个结点,移动6-4
次删除第i个结点,移动n-i
次【算法步骤】(1)判断删除位置i是否合法(合法值为1≤i≤n)。(2)将欲删除的元素保留在e中。(3)将第i+1至第n位的元素依次向前移动一个位置。(4)表长减1,删除成功返回OK。StatusListDelete_Sq(SqList&L,inti){if((i<1)||(i>L.length))returnERROR; //i值不合法
for(j=i;j<=L.length-1;j++)
L.elem[j-1]=L.elem[j];//被删除元素之后的元素前移
--L.length; //表长减1returnOK;}【算法描述】将线性表L中第i个数据元素删除若删除尾结点,则根本无需移动(特别快);若删除首结点,则表中n-1个元素全部前移(特别慢);若要考虑在各种位置删除(共n种可能)的平均移动次数,该如何计算?算法时间主要耗费在移动元素的操作上【算法分析】123显然,顺序表的空间复杂度S(n)=O(1)(没有占用辅助空间)查找、插入、删除算法的平均时间复杂度为:O(n)顺序表(顺序存储结构)的特点这种存取元素的方法被称为随机存取法利用数据元素的存储位置表示线性表中相邻数据元素之间的前后关系,即线性表的逻辑结构与存储结构一致在访问线性表时,可以快速地计算出任何一个数据元素的存储地址。因此可以粗略地认为,访问每个元素所花时间相等
(1)(2)顺序表的优缺点链表为克服这一缺点时间:可以随机存取表中任一元素空间:存储密度大(结点本身所占存储量/结点结构所占存储量)优点:时间:在插入、删除某一元素时,需要移动大量元素空间:浪费存储空间,属于静态存储形式,数据元素的个数不能自由扩充缺点:线性表的链式表示和实现链式存储结构结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻线性表的链式表示又称为非顺序映像或链式映像。如何实现?通过指针来实现单链表的存储映像(a)可利用存储空间a0a2a1a3
freefirst(b)经过一段运行后的单链表结构线性表的链式表示和实现例画出26个英文字母表的链式存储结构链式存储结构:逻辑结构:(a,b,…,y,z)aheadb/\z……各结点由两个域组成:数据域:存储元素数值数据指针域:存储直接后继结点的存储位置指针数据例画出26个英文字母表的链式存储结构与链式存储有关的术语1、结点:数据元素的存储映像。由数据域和指针域两部分组成2、链表:
n个结点由指针链组成一个链表。它是线性表的链式存储映像,称为线性表的链式存储结构a1heada2an……head循环链表示意图:3、单链表、双链表、循环链表:
结点只有一个指针域的链表,称为单链表或线性链表有两个指针域的链表,称为双链表首尾相接的链表称为循环链表与链式存储有关的术语4、头指针、头结点和首元结点头指针是指向链表中第一个结点的指针首元结点是指链表中存储第一个数据元素a1的结点头结点是在链表的首元结点之前附设的一个结点;数据域内只放空表标志和表长等信息与链式存储有关的术语头指针头结点首元结点a1heada2…infoan^上例链表的逻辑结构示意图有以下两种形式:①ZHAOQIANLISUNZHOUWUZHENG/\WANGH②ZHAOQIANLISUNZHOUWUZHENG/\WANGH区别:①
无头结点②有头结点与链式存储有关的术语讨论1.如何表示空表?有头结点时,当头结点的指针域为空时表示空表非空表
空表0ana0headhead^表头结点第一个结点与链式存储有关的术语讨论2.在链表中设置头结点有什么好处?⒈便于首元结点的处理首元结点的地址保存在头结点的指针域中,所以在链表的第一个位置上的操作和其它位置一致,无须进行特殊处理;⒉便于空表和非空表的统一处理无论链表是否为空,头指针都是指向头结点的非空指针,因此空表和非空表的处理也就统一了。与链式存储有关的术语讨论3.头结点的数据域内装的是什么?
头结点的数据域可以为空,也可存放线性表长度等附加信息,但此结点不能计入链表长度值。头结点的数据域H与链式存储有关的术语结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻链表(链式存储结构)的特点访问时只能通过头指针进入链表,并通过每个结点的指针域向后扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等
这种存取元素的方法被称为顺序存取法01OPTION02OPTION链表的优缺点优点时间:插入、删除等操作不必移动数据,只需修改链接指针,修改效率较高空间:数据元素的个数可以自由扩充缺点时间:存取效率不高,必须采用顺序存取,即存取数据元素时,只能按链表的顺序进行访问(顺藤摸瓜)空间:存储密度小链表的优缺点练习1.链表的每个结点中都恰好包含一个指针。2.顺序表结构适宜于进行顺序存取,而链表适宜于进行随机存取。3.顺序存储方式的优点是存储密度大,且插入、删除运算效率高。4.线性表若采用链式存储时,结点之间和结点内部的存储空间都是可以不连续的。5.线性表的每个结点只能是一个简单类型,而链表的每个结点可以是一个复杂类型×
单链表的定义和实现非空表空表单链表是由表头唯一确定,因此单链表可以用头指针的
名字来命名若头指针名是L,则把链表称为表LtypedefstructLNode{ElemTypedata;//数据域
structLNode*next;//指针域}LNode,*LinkList;//*LinkList为LNode类型的指针单链表的存储结构定义LNode*pLinkList
pLNode*p注意区分指针变量和结点变量两个不同的概念若p->data=ai,则p->next->data=ai+1单链表的存储结构定义指针变量p:表示结点地址结点变量*p:表示一个结点【算法步骤】【算法描述】初始化(构造一个空表)StatusInitList_L(LinkList&L){
L=newLNode; L->next=NULL;
returnOK;}(1)生成新结点作头结点,用头指针L指向头结点。(2)头结点的指针域置空。StatusDestroyList_L(LinkList&L){LinkListp;while(L){p=L;L=L->next;deletep;}returnOK;}补充:几个简单基本操作的算法实现销毁StatusClearList(LinkList&L){
//将L重置为空表
LinkListp,q;p=L->next;//p指向首元结点
while(p)//没到表尾
{q=p->next;deletep;p=q;}L->next=NULL;//头结点指针域为空
returnOK;}补充:几个简单基本操作的算法实现清空pLa1a2…...^pi01p2pn==NULLan求表长p=L->next;i=0;while(p){i++;p=p->next;}补充:几个简单基本操作的算法实现“数”结点:指针p依次指向各个结点从第一个元素开始“数”一直“数”到最后一个结点求表长intListLength_L(LinkListL){//返回L中数据元素个数
LinkListp;
p=L->next;//p指向第一个结点
i=0;
while(p){//遍历单链表,统计结点数
i++;
p=p->next;}
returni;}“数”结点:指针p依次指向各个结点从第一个元素开始“数”一直“数”到最后一个结点补充:几个简单基本操作的算法实现判断表是否为空intListEmpty(LinkListL){ //若L为空表,则返回1,否则返回0
if(L->next)
//非空return0;elsereturn1;}补充:几个简单基本操作的算法实现思考:顺序表里如何找到第i个元素?链表的查找:要从链表的头指针出发,顺着链域next逐个结点往下搜索,直至搜索到第i个结点为止。因此,链表不是随机存取结构取值(根据位置i获取相应位置数据元素的内容)L211830754256∧pppj123p1i=3i=156p7例:分别取出表中i=3和i=15的元素从第1个结点(L->next)顺链扫描,用指针p指向当前扫描到的结点,p初值p
=
L->next。j做计数器,累计当前扫描过的结点数,j初值为1。当p指向扫描到的下一结点时,计数器j加1。当j
=
i时,p所指的结点就是要找的第i个结点。【算法步骤】线性表的重要基本操作p//获取线性表L中的某个数据元素的内容StatusGetElem_L(LinkListL,inti,ElemType&e){p=L->next;j=1;//初始化
while(p&&j<i){ //向后扫描,直到p指向第i个元素或p为空
p=p->next;++j;}if(!p||j>i)returnERROR;//第i个元素不存在
e=p->data;//取第i个元素
returnOK;}//GetElem_L取值(根据位置i获取相应位置数据元素的内容)查找(根据指定数据获取数据所在的位置)L211830753056∧pj1x=30p2p3找到,返回ix=51p1p6p7未找到,返回0从第一个结点起,依次和e相比较。如果找到一个其值与e相等的数据元素,则返回其在链表中
的“位置”或地址;如果查遍整个链表都没有找到其值和e相等的元素,则返回0
或“NULL”。//在线性表L中查找值为e的数据元素LNode*LocateELem_L(LinkListL,Elemtypee){//返回L中值为e的数据元素的地址,查找失败返回NULLp=L->next;while(p&&p->data!=e)p=p->next; returnp; }【算法描述】线性表的重要基本操作//在线性表L中查找值为e的数据元素intLocateELem_L(LinkListL,Elemtypee){//返回L中值为e的数据元素的位置序号,查找失败返回0
p=L->next;j=1;while(p&&p->data!=e){p=p->next;j++;} if(p)returnj;elsereturn0;}【算法描述】线性表的重要基本操作将值为x的新结点插入到表的第i个结点的位置上,即插入到ai-1与ai之间s->next=p->next;p->next=s思考:步骤1和2能互换么?插入(插在第i个结点之前)
15:11
【算法步骤】找到ai-1存储位置p线性表的重要基本操作01OPTION02OPTION03OPTION新结点*s的指针域指向结点ai令结点*p的指针域指向新结点*s将新结点*s的数据域置为x生成一个新结点*s04OPTION05OPTION//在L中第i个元素之前插入数据元素e
StatusListInsert_L(LinkList&L,inti,ElemTypee){p=L;j=0;
while(p&&j<i−1){p=p->next;++j;} //寻找第i−1个结点
if(!p||j>i−1)returnERROR; //i大于表长
+
1或者小于1s=newLNode; //生成新结点ss->data=e; //将结点s的数据域置为e
s->next=p->next; //将结点s插入L中
p->next=s;returnOK;}//ListInsert_L【算法描述】线性表的重要基本操作(1)找到ai-1存储位置p(2)保存要删除的结点的值(3)令p->next指向ai的直接后继结点(4)释放结点ai的空间删除(删除第i个结点)将表的第i个结点删去步骤:p->next=p->next->next???
ai-1ai-1aiaiai+1ai+1pq删除前删除后删除(删除第i个结点)【算法步骤】(1)找到ai-1存储位置p(2)临时保存结点ai的地址在q中,以备释放(3)令p->next指向ai的直接后继结点(4)将ai的值保留在e中(5)释放ai的空间删除(删除第i个结点)q//将线性表L中第i个数据元素删除
StatusListDelete_L(LinkList&L,inti,ElemType&e){p=L;j=0;while(p->next&&j<i-1){//寻找第i-1个结点,并令p指向其前驱
p=p->next;++j;}if(!(p->next)||j>i-1)returnERROR;//删除位置不合理
q=p->next;//临时保存被删结点的地址以备释放
p->next=q->next; //改变删除结点前驱结点的指针域
e=q->data; //保存删除结点的数据域
deleteq; //释放删除结点的空间
returnOK;}//ListDelete_L【算法描述】删除(删除第i个结点)但是,如果要在单链表中进行前插或删除操作,由于要从头查找前驱结点,所耗时间复杂度为O(n)。链表的运算时间效率分析1.查找:
因线性链表只能顺序存取,即在查找时要从头指针找起,查找的时间复杂度为
O(n)。2.插入和删除:
因线性链表不需要移动元素,只要修改指针,一般情况下时间复杂度为
O(1)。从一个空表开始,重复读入数据:生成新结点将读入数据存放到新结点的数据域中将该新结点插入到链表的前端单链表的建立(前插法)p->data=anp->data=an-1L->next=pp->next=L->next单链表的建立(前插法)voidCreateList_F(LinkList&L,intn){L=newLNode;L->next=NULL;//先建立一个带头结点的单链表
for(i=n;i>0;--i){p=newLNode;//生成新结点
cin>>p->data;//输入元素值
p->next=L->next;L->next=p; //插入到表头
}}//CreateList_F【算法描述】单链表的建立(前插法)从一个空表L开始,将新结点逐个插入到链表的尾部,尾指针r指向链表的尾结点。初始时,r同L均指向头结点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,r指向新结点。单链表的建立(尾插法)从空表L开始,将新结点逐个插入到链表的尾部,尾指针r指向链表的尾结点。初始时,r同L均指向头结点。每读入一个数据元素则申请一个新结点,将新结点插入到尾结点后,r指向新结点。单链表的建立(尾插法)voidCreateList_L(LinkList&L,intn){//正位序输入n个元素的值,建立带表头结点的单链表LL=newLNode;L->next=NULL;
r=L; //尾指针r指向头结点
for(i=0;i<n;++i){p=newLNode;
//生成新结点
cin>>p->data; //输入元素值
p->next=NULL;r->next=p;//插入到表尾
r=p; //r指向新的尾结点
}}//CreateList_L【算法描述】单链表的建立(尾插法)循环链表L->next=L(a)非空单循环链表L(b)空表L说明从循环链表中的任何一个结点的位置都可以找到其他所有结点,而单链表做不到;循环条件:p!=NULLp!=Lp->next!=NULLp->next!=L循环链表中没有明显的尾端如何避免死循环循环链表对循环链表,有时不给出头指针,而给出尾指针可以更方便的找到第一个和最后一个结点rear
a1
ai-1
an
ai如何查找开始结点和终端结点?开始结点:rear->next->next终端结点:rear说明循环链表Taa1anTbb1bn两个循环链表的合并a1anb1bn①p②③④TaTb示例循环链表a1anb1bn①p②③④TaTbLinkListConnect(LinkListTa,LinkListTb){//假设Ta、Tb都是非空的单循环链表//①p存表头结点//②Tb表头连结Ta表尾//③释放Tb表头结点//④修改指针returnTb;}p=Ta->next;Ta->next=Tb->next->next;deleteTb->next;
Tb->next=p;
示例循环链表著名犹太历史学家
Josephus约瑟夫问题在罗马人占领乔塔帕特后39个犹太人与Josephus及他的朋友躲到一个洞中39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止然而Josephus和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏例如n=8m=3约瑟夫问题约瑟夫问题的解法voidJosephus(intn,intm){Firster();//检验指针指向第一个结点
for(inti=0;i<n-1;i++){
//执行n-1次
for(intj=0;j<m-1;j++)Next();
//循环m次使current指向被删除结点
cout<<“出列的人是”<<GetElem_L()<<endl;
//出列人员的数据ListDelete();
//删去每一趟的第m结点
}约瑟夫问题typedefstructDuLNode{ElemTypedata;structDuLNode*prior;structDuLNode*next;}DuLNode,*DuLinkList双向链表(a)空双向循环链表(b)双向循环链表d->next->prior=d->prior->next=dL->next=L双向链表双向链表的插入双向链表的插入abx......1ps1.s->prior=p->prior;2.p->prior->next=s;abx......12ps双向链表的插入1.s->prior=p->prior;双向链表的插入032.p->prior->next=s;1.s->prior=p->prior;3.s->next=p;4.p->prior=s;abx......1234ps双向链表的插入2.p->prior->next=s;1.s->prior=p->prior;3.s->next=p;StatusListInsert_DuL(DuLinkList&L,inti,ElemTypee){if(!(p=GetElemP_DuL(L,i)))returnERROR;s=newDuLNode;s->data=e;
s->prior=p->prior;p->prior->next=s;s->next=p;p->prior=s;returnOK;}双向链表的插入双向链表的删除1.p->prior->next=p->next;双向链表的删除1.p->prior->next=p->next;2.p->next->prior=p->prior;StatusListDelete_DuL(DuLinkList&L,inti,ElemType&e){if(!(p=GetElemP_DuL(L,i)))returnERROR;e=p->data;
p->prior->next=p->next;p->next->prior=p->prior;deletep;returnOK;}双向链表的删除顺序表和链表的比较
存储结构
比较项目顺序表链表空间存储空间预先分配,会导致空间闲置或溢出现象动态分配,不会出现存储空间闲置或溢出现象存储密度不用为表示结点间的逻辑关系而增加额外的存储开销,存储密度等于1需要借助指针来体现元素间的逻辑关系,存储密度小于1时间存取元素随机存取,按位置访问元素的时间复杂度为O(1)顺序存取,按位置访问元素时间复杂度为O(n)插入、删除平均移动约表中一半元素,时间复杂度为O(n)不需移动元素,确定插入、删除位置后,时间复杂度为O(1)适用情况①表长变化不大,且能事先确定变化的范围②很少进行插入或删除操作,经常按元素位置序号访问数据元素①长度变化较大②频繁进行插入或删除操作线性表的应用有序表的合并线性表的合并线性表的合并
问题描述:假设利用两个线性表La和Lb分别表示两个集合
A和B,现要求一个新的集合
A=ABLa=(7,5,3,11)Lb=(2,6,3)La=(7,5,3,11,2,6)依次取出Lb中的每个元素,执行以下操作:【算法步骤】
1.在La中查找该元素
2.如果找不到,则将其插入La的最后voidunion(List&La,ListLb){La_len=ListLength(La);Lb_len=ListLength(Lb);
for(i=1;i<=Lb_len;i++){
GetElem(Lb,i,e);if(!LocateElem(La,e))
ListInsert(&La,++La_len,e);}}【算法描述】问题描述:La=(1,7,8)Lb=(2,4,6,8,10,11)Lc=(1,2,4,6,7,8,8,10,11)有序表的合并已知线性表La和Lb中的数据元素按值非递减有序排列,现要求将La和Lb归并为一个新的线性表Lc,且Lc中的数据元素仍按值非递减有序排列。【算法步骤】-有序的顺序表合并依次从La或Lb中“摘取”元素值较小的结点插入到Lc表的最后,直至其中一个表变空为止0201创建一个空表Lc继续将La或Lb其中一个表的剩余结点插入在Lc表的最后03voidMergeList_Sq(SqListLA,SqListLB,SqList&LC){pa=LA.elem;pb=LB.elem;//指针pa和pb的初值分别指向两个表的第一个元素
LC.length=LA.length+LB.length; //新表长度为待合并两表的长度之和
LC.elem=newElemType[LC.length]; //为合并后的新表分配一个数组空间
pc=LC.elem; //指针pc指向新表的第一个元素
pa_last=LA.elem+LA.length-1; //指针pa_last指向LA表的最后一个元素
pb_last=LB.elem+LB.length-1; //指针pb_last指向LB表的最后一个元素
while(pa<=pa_last&&pb<=pb_last){ //两个表都非空
if(*pa<=*pb)*pc++=*pa++; //依次“摘取”两表中值较小的结点
else*pc++=*pb++;}pa++;
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年教育系统公开选拔学校年轻后备干部考试参考试题及答案
- 2026年矿山救护队技能理论考试题库带答案
- 2026年煤矿青工模拟试卷带答案
- 2026年农机驾照题库及参考答案
- 2026年全国监理工程师考试真题及答案
- 2026年人工智能训练师(高级)职业技能鉴定参考题库含答案
- 2026年全科主治医师考试试题
- 2026年10月高等教育自学考试《中级财务会计》模拟试卷A含完整答案解析
- 光学基础及其技术 11
- 2026年福建南平市中考二模化学试题(含答案)
- 旋风分离器设计计算表-自动计算版(带公式自动计算版)
- 26版一上语文全册每课一练(含答案)
- 警棍盾牌操图文教材
- 医务科医疗质量改进与安全管理工作计划
- 工业仿真软件基础教程245
- 2026年工伤事故预防培训试题及答案
- 实施指南(2026)《JBT 7364-2014倍速输送链和链轮》
- 医院临床科研能力提升
- 三腔二囊管的护理查房
- 浙江润彩新材料科技有限公司年产23000吨消泡剂和7000吨润湿剂项目环评报告
- 非标设备项目管理制度
评论
0/150
提交评论