数据结构学习提纲
(最终解释权归原作者所有,侵权必究)
绪论
1、【概念】数据、数据元素、数据项、数据对象、数据结构
数据:能被输入到计算机中,能被计算机识别,且能被计算机加工处理的符号集合,是计算机操作对象的总称。
数据元素:数据的一个基本单位。如果数据元素被组织成表结构,也称其为数据记录。
数据项:构成数据元素的单位,可以是原子项,也可以是组合项。
数据对象:性质相同的数据元素的集合,是数据的一个子集。
数据结构:关系,相互之间存在着某种逻辑关系的数据元素的集合。
数据包含数据对象,数据对象包含数据元素,数据元素包含数据项。
2、【概念】数据的逻辑结构和存储结构
逻辑结构:对数据元素的逻辑关系的描述,常用图形表示法表示,包括集合结构、线性结构、树形结构、图形结构。
存储结构(物理结构);需考虑数据的存储和关系的存储,包括顺序存储和链式存储。
3、【概念】抽象数据类型(ADT:Abstract Data Type)
数据类型:一个值的集合以及定义在这个值集上的一组操作的总称。
抽象数据类型:一个数学模型以及定义在该模型上的一组操作,只取决于逻辑。具有数据抽象和数据封装两个特性。可用三元组(D,S,P)表示,其中D是数据对象,S是D上的关系集,P是对D的基本操作集。
4、【概念】算法
问题求解所需要的具体步骤和方法,是规定的一个有限长的操作序列。具有0或多个输入,1或多个输出,确定性,有限性,有效性五大特性。算法效率分析方法有事后统计法和事前分析估算法。对于一个算法,其时间效率和空间效率往往是相互影响的,当追求较高的时间性能时,可能会导致更高的空间复杂度。
5、【概念】时间复杂度
算法所耗费的时间 = 算法中每条语句的执行时间之和
每条语句的执行时间 = 语句的频度 * 语句执行一次所需时间
算法的运行时间主要取决于算法选用的策略,问题的规模,编写程序的语言,编译程序产生的机器代码质量,计算机执行指令的速度。一般情况下,算法中的基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为非零的常数,则成f(n)是T(n)的同数量级函数,即T(n)的增长率与f(n)的增长率相同。记作T(n)=O(f(n)),则称O(f(n))为算法的渐进时间复杂度。算法分析中不区分算法的时间复杂度和渐进时间复杂度。常见的时间复杂度按递增排列为O(1)、O(log n)、O(n)、O(nlogn)、O(n^2)、O(n^3)、……、k次方阶O(n^k)、指数阶O(2^n)
6、【概念】空间复杂度
算法的空间消耗包括存储算法本身所占用的存储空间,算法的输入输出数据所占用的存储空间,以及算法在运行过程中临时占用的存储空间。
S(n)=O(g(n))也是问题规模的函数,表示随着问题规模n的增大,算法运行所需存储量的增长率与g(n)的增长率相同。
线性表
1、【概念】线性表
最简单的一种线性结构。具有如下特征:集合中必存在唯一的第一元素,集合中必存在唯一的最后元素,除最后元素在外,均有唯一的直接后继,除第一元素之外,均有唯一的直接前驱。可表示为L(D,R),D是数据元素的集合,R是数据元素关系的集合R={
2、【概念】线性表的存储
顺序存储:一片连续存储空间(数组或数组起始地址,用于存放数据元素)、线性表的容量(数组大小,防止溢出)、线性表的长度(已存入到数组中的数据元素个数)
链式存储:单向链表(带头结点、不带头结点)
3、【实践】线性表的基本操作
顺序存储线性表:初始化、插入、删除、更新、获取、定位、遍历
链式存储线性表:初始化、插入、删除、更新、获取、定位、求长度、遍历、创建(初始化+插入,头插法,尾插法)
注:三种创建方法的比较:调用插入函数效率最低,头插链表中数据与输入相反,尾插链表中数据与输入相同。
4、【概念】静态链表
对于某些程序设计语言,不支持指针变量,可以用数组模拟单向链表,称为静态链表。其思想是开辟一个较大容量的一维结构体数组,每个数组元素是一个结点,包含一个数据域和一个指针域,指针域的类型为int型,存放下一个结点的下标。为了便于插入和删除,将一维结构体数组的数组元素分为两组,已经存放数据的数据元素组成一个链表,未存放数据的结点组成另一个备用链表,用于提供插入时的新结点,回收删除时的结点。
定义:typedefstruct{ElemType data; int next;}SNode;
typedef struct{SNode sd[MAX];int SL,AV;intSLinksize;}StaticLink;
5、【实践】静态链表的基本操作
初始化、插入、删除、更新、获取、定位、遍历、求长度 □
6、【概念】线性表两种存储结构的区别
顺序存储:数据元素的存储空间是连续的,只要知道首地址,任意一个元素的地址都可以根据下标直接计算,即顺序存储,随机存取,存储密度=1。
链式存储:数据元素所在的结点是动态申请的,任意一个数据元素都要通过头指针开始查找,即随机存储,顺序存取,存储密度<1。
注:存储密度:按数据元素类型分配空间大小/数据元素所在结点分配空间大小
实际应用中:基于空间考虑:如果线性表的长度事先可确定,宜采用顺序表;如果线性表的长度变化较大,宜采用链表。基于时间考虑:如果经常对线性表做插入和删除操作,宜采用链表;反之采用顺序表。
7、【实践】其他形式的链表的基本操作
单向循环链表(带头指针 □、带尾指针 □):初始化、插入、删除
双向循环链表:初始化、插入、删除、遍历 □
8、【实践】线性表的应用
两个线性表的合并(在原表中插入合并 □、合并成一新表 □)
一元多项式的应用(插入、创建、加法、乘法、显示)□
9、【实践】相关算法设计题
⑴ 已知一顺序表A,其元素值非递减有序排列,编写一个函数删除顺序表中多余的值相同的元素。 □
⑵ 编写一个函数,从一给定的顺序表A中删除x~y(x<=y)之间的所有元素,要求以较高的效率来实现。 □ (提示:将顺序表中所有值在x~y之间的元素置成一个特殊的值,并不立即删除它们,然后最后向前依次扫描,发现具有特殊值的元素,移动其后面的元素将其删除)
⑶ 线性表中有n个元素,每个元素是一个字符,现存于数组R[n]中,试写一算法,使R中的字符按字符按字母字符、数字字符和其他字符的顺序排列。要求利用原来的存储空间,元素移动次数最小。□
⑷ 线性表用顺序存储,设计一个算法,用尽可能少的辅助存储空间将顺序表中前m个元素和后n个元素进行整体互换。 □
⑸ 写出将线性表就地逆转算法,要求:分别用顺序表和带头结点的单链表来实现。 □
⑹ 已知带头结点的单链表L中的结点是按整数值递增排列的,试写一算法,将值为x的结点插入到表L中,使得L仍然有序,并且分析算法的时间复杂度。 □
⑺ 假设有两个已排序的单链表A和B,编写一个函数将他们合并成一个链表C而不改变其排序性。
⑻ 假设有两个已排序的顺序表A和B,编写一个函数将他们合并成一个顺序表C而不改变其排序性。 □
⑼ 假设长度大于1的循环单链表中,既无头结点也无头指针,p为指向该链表中某一结点的指针,编写一个函数删除该结点的前驱结点。 □
⑽ 已知两个单链表A和B分别表示两个集合,其元素递增排列,编写一个函数求出A和B的交集C,要求C同样以元素递增的单链表形式存在。 □
⑾ 试编写一个算法,将一个用带头结点的单向链表表示的多项式分解成两个多项式,使这两个多项式分别仅含奇次指数项或偶次指数项。要求利用原链表的结点存储空间。 □
⑿ 已知p指向双向循环链表中的一个结点,其结点结构为data、llink、rlink三个域,写出算法change(p),交换p所指向的结点和它的前驱结点的顺序。 □
⒀ 已知不带头结点的线性链表list,链表中结点构造为(data、link),其中data为数据域,link为指针域。请写一算法,将该链表按结点数据域的值的大小从小到大重新链接。要求链接过程中不得使用除该链表以外的任何结点空间。 □
⒁ 设键盘输入n个英语单词,输入格式为n,w1,w2,……,wn,其中n表示随后输入英语单词个数,试编一程序,建立一个单向链表,实现:如果单词重复出现,则只在链表上保留一个;链表结点还应有一个计数域,记录该单词重复出现的次数,然后输出出现次数最多的前k(k<=n)个单词。 □
⒂ Josephu问题:设编号为1,2,…,n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。 □
栈与队列
1、【概念】栈
一种特殊的线性表,限定插入和删除操作只能在一端进行。具有后进先出的特点。栈顶(TOP):允许插入和删除的一端,栈底(BOTTOM):不允许插入和删除的一端。
2、【概念】栈的存储
顺序栈:一片连续存储空间(数组或数组起始地址,用于存放数据元素),int top指示栈顶的位置,int size栈的容量
链栈:不带头结点的单向链表
3、【实践】栈的基本操作
初始化、销毁、判断栈空、得到栈顶、清空栈、求栈的长度、进栈、出栈、遍历□ □
4、【实践】栈的应用
基于栈的括号匹配□、表达式求值(转为后缀表达式后求值□、直接求值□)、递归算法(Fibnacci数列□、逆序输出不带头结点的单链表□、Hanoi塔□)
注:递归转为非递归:直接转换法、间接转换法(递归调用语句进栈、递归返回调用的后续执行语句出栈);函数的调用都是通过栈来实现的;递归条件:规模确定、有递归出口、确定一般公式。
5、【概念】队列
一种特殊的线性表,限定插入操作在线性表的一端,删除操作在线性表的另一端。具有先进先出的特点。队头:允许删除的一端,队尾:允许插入的一端。
6、【概念】队列的实际应用
解决由多用户(多终端)引起的资源竞争问题;解决主机与外部设备之间的速度不匹配问题。
7、【概念】队列的存储
顺序队列:由于队列的插入删除分别被限制在队头队尾,因此必须知道队头队尾位置,如果规定队头指针指向队头,队尾指针指向队尾,会引起进队、出队、对空操作的二义性,所以通常规定队头指针指向队头,队尾指针指向当前队尾的下一个元素。但当队尾指针指向数组最后一个元素时,空间“溢出”,但此前有若干元素出队列,一部分空间是闲置的,这种现象被称为“假溢出”。解决方案:用一个变量记当前队列的长度:int front;指向队头的位置,int rear;指向队尾的下一个元素的位置,int size;队列的容量;QElemType data[MAX];数据;int length;当前长度。循环队列(队列长度比循环队列的容量少1):int front;指向队头的位置,int rear;指向队尾的下一个元素的位置,int size;队列的容量;QElemType data[MAX];数据
链队列:用带头结点的单向链表存放队列中的数据元素,用一个头指针记住链表的头结点,一个尾指针记住队尾。
8、【实践】队列的基本操作
初始化、销毁、判断队空、求队列长度、得到队头、清空、进队列、出队列、遍历□ □
9、【实践】队列的应用
划分子集问题(运动会排时间):设定项目报名 项目编排 显示 □
10、【概念】共用栈
实际应用中,有时一个应用程序需要多个栈,但这些栈的数据元素类型相同。假设每个栈采用顺序栈,由于每个栈的使用情况不尽相同,势必会造成存储空间的浪费。若让多个栈共用一个足够大的连续存储空间,则可利用栈的动态特性使它们的存储空间互补,这时的操作必须同时记住多个栈的栈顶。
为使操作更加方便,可采用多个单链栈,将多个单链栈的栈顶指针存放到一个指针数组中,顺序栈的共享最常见的是两栈的共享。
11、【实践】共用栈的基本操作
初始化、销毁、判断栈空、得到栈顶、清空栈、求栈的长度、进栈、出栈、遍历□ □
12、【概念】双端队列
如果限定插入和删除均可以在线性表的两端进行,则成为双端队列。这样的结构常用于CPU调度。实际应用中,也可对双端队列做如下限制:只允许在一端进行插入,两端进行删除;只允许在一端进行删除,两端进行插入。
13、【实践】双端队列的基本操作
初始化、判断队空、求队列长度、得到队头、清空、进队列、出队列、遍历□ □ □ □
14、【实践】相关算法设计题
⑴ 要求循环队列的空间全部都能得到利用,设置标识域tag,以tag为0或1来区分头尾指针相同时队列状态的空与满,试编写与此结构相适应的入队与出队算法。 □
⑵ 正读与反读都相同的字符序列称为“回文”序列。试编写一个算法,判断一次读入以“@”结束的字母序列,是否为形如“序列1&序列2”模式的字符序列。其中序列1和序列2中都不含有字符“&”,且序列2是序列1的逆序列。要求用栈和队列来实现。□
⑶ 数值转换。编写程序,将十进制整数N转换为d进制数,其转换步骤是重复以下两歩,直到N等于0。X=N mod d(其中mod为求余运算),N=N div d(其中div为整除运算)□
⑷ 商品货架可以看做是个栈,栈顶商品的生产日期最早,栈底商品的生产日期最近。上货时,以保证生产日期较近的商品放在较下的位置,用另一个栈作为周转,模拟实现商品货架管理过程。□
⑸ 试写出求递归函数F(n)的递归算法,并消除递归:F(n)=n+1(n=0),m*F(n/2)(n>0)□
⑹ 求两个数的最大公约数、最小公倍数,要求用递归算法实现。□
⑺ 请利用两个栈s1和s2来模拟一个队列。已知栈的三个运算定义如下:PUSH(ST,x):元素x入ST栈;POP(ST,x):ST栈顶元素出栈,赋给变量x;Sempty(ST):判ST栈是否为空。那么如何利用栈的运算来实现该队列的三个运算:EnQueue():插入一个元素入队列;DeQueue():删除一个元素出队列;Queue_Empty():判队列为空。□
⑻ 假设循环队列中front指向队头元素的前一个位置,rear指向队尾元素。试编写相应的入队、出队以及判断队空、队满的函数。□
⑼ 写一个算法,借助于栈将一个单链表逆置。 □
⑽ 在行编辑程序中,设用户输入一行的过程中允许用户输入出差错,并在发现有误时通过“#”(退格符)和“@”(退行符)进行改正,当输入回车时处理所有的输入字符得到最终的输入,试编写算法实现行输入处理过程。□
⑾ 完成对任意多位整数和实数的算术表达式运算,要求:用顺序栈检查表达式中的括号是否匹配,如果匹配,才计算表达式的值;用链栈完成先求后缀表达式再求值;;用链栈完成直接求值。□ □ □ □
⑿ 模拟银行排队等候叫号系统,采用循环队列完成。系统具有如下功能:拿号等候、窗口1叫号、窗口2叫号、显示等候的人数。 □
数组和特殊矩阵
1、【概念】数组的内存映像
一维数组元素的存储地址:首元素地址+下标*每个元素存储占用的字节数
二维数组元素的存储地址:LOC(aij) = LOC(a00) + (i*n+j) * L
三维数组元素的存储地址:LOC(aijk) = LOC(a000) + (i*n*p+j*p+k) * L
2、【实践】数组内存映像练习,不用下标,用地址
若矩阵Am*n中存在某个元素aij满足:aij是第i行中最小值且是第j列中的最大值,则称该元素为矩阵A中的一个鞍点。试编写一个算法,找出A中的所有鞍点。 □
求Amnp的最大值。□
3、【概念】对称矩阵的压缩存储
将下三角部分的所有元素以行为主序顺序存储到一个一维数组中,对于下三角元素,其下标k=i*(i+1)/2+j,对于上三角元素,其下标k=j*(j+1)/2+i
4、【概念】三角矩阵的压缩存储
下三角矩阵:将下三角部分的所有元素以行为主序顺序存储到一个一维数组中,紧接着存储对角线上方的常量c,对于下三角部分k=i*(i+1)/2+j,上三角部分k=n*(n+1)/2。
上三角矩阵:将上三角部分的所有元素以行为主序顺序存储到一个一维数组中,紧接着存储对角线下方的常量c,对于下三角部分k=m*(n+1)/2,上三角部分k=i*(2n-i+1)/2+j-i
5、【概念】带状矩阵的压缩存储
n阶矩阵A称为带状矩阵,如果存在最小正数m,满足当|i-j|≥m时,aij=0,这时称 w=2m-1为矩阵A的带宽。特点是所有非零元素都集中在以主对角线为中心的带状区域中,所有其他元素为0。
压缩方法:① 将A压缩存储到一个n行w列的二维数组B中,即带宽为区域内的元素立起来,缺失部分补0,A[i][j]映射B[m][n]映射关系为m=i,n=j-i+1 ② 将带状矩阵A压缩到一个一维数组中,按以行为主序,顺序存储其非零元素
6、【实践】矩阵基本操作
编写创建n阶整形对称矩阵A和输出n阶整形对称矩阵的算法。 □
编写创建n阶整形三角矩阵A和输出n阶整形三角矩阵的算法。 □
编写创建n阶w带宽整形带状矩阵A和输出n阶w带宽整形带状矩阵的算法。 □
7、【概念】稀疏矩阵的三元组表存储
将非零元素所在的行、列以及它的值构成一个三元组(i,j,v),按行优先的顺序,同一行中列号从小到大的规律排列成一个线性表,即三元组表。
typedefstruct { int I,j; datatype v;} SPNode; //三元组类型:非零元素的行、列、值
typedefstruct {SPNode data[MAX]; int mu,nu,tu;} SPMatrix; //三元组表:数据、行数、列数、非零元素数
8、【实践】稀疏矩阵的三元组表基本操作
创建、展示、转置(原始、优化)、乘积 □
9、【概念】稀疏矩阵的十字链表存储
对每个非零元素存储为一个结点,结点由5个域组成,其中row存储非零元素的行号,col存储非零元素的列号,v域存储本元素的值,right,down是两个指针域。稀疏矩阵中每一行的非零元素结点按照其列号从小到大顺序由right域链成一个带表头结点的循环行链表,同样每一列中的非零元素按其行号从小到大顺序由down域链成一个带表头结点的循环列链表。
10、【实践】稀疏矩阵的十字链表基本操作
创建、展示、加法 □
11、【实践】相关算法设计题
设计一个算法,将数组A[n]中的元素A[0]至A[n-1]循环右移k位,并要求只用一个元素大小的附加存储,元素移动或交换次数为O(n) □