Class No:1绪论
一:基本概念与术语
数据:能输入到计算机中并被计算机程序处理的符号的总称
数据元素:是数据的基本单位;如一位学生的关于学号,姓名等所有记录
数据项:是组成数据元素的不可分割的最小单位;如一个学生的学号或姓名.......
数据对象:性质相同的数据元素的集合,是数据的子集,如总学生信息表
关键码:数据元素中起标识作用的数据项;如学号,年龄这些专有名词
关系:集合中数据元素之间的联系
数据结构:
: 相互之间存在一种或多种特定关系的数据元素的集合
:带“结构”的数据元素的集合,“结构”指的是关系
数据结构的表达:
数据结构的形式定义:二元组:DS=(S,R)
S-数据元素的集合 ;R-S上关系的有限集合
例:一个家谱图 S={A,B,C,D,E,F,G}
家庭成员 R={<A,B><A,G><D,B><D,C><G,E><G,F>}父子关系
- 逻辑结构:(从逻辑关系出发)
线性结构:数据元素之间存在一对一的关系
非线性结构:
集合结构:数据元素之间有且仅有“属于同一集合”的关系
树结构:数据元素之间存在一对多的关系。注:树的下端不会“饱和”
图结构或网状结构:数据元素之间存在多对多的关系
- 存储结构:数据对象在计算机中的存储,既要存储各数据元素的数据,又要存储数据元素之间 的逻辑关系(类似结点)
顺序存储结构:数据元素的存储位置体现数据元素相互间的逻辑关系
元素都依次存放再连续的存储空间
链式存储结构:存储单元可以是连续的也可以是不连续的。
索引存储结构:一般形式是:<关键字,地址>
关键字:标识唯一一个结点
地址:作为指向结点的指针。
散列存储结构:结点的关键字通过散列函数直接计算结点的存储地址。
- 数据运算:对数据能进行的操作,合理的操作构成数据结构的运算集合,如检索、排序、插入、删除、修改等 ,不同的存储结构,在数据运算上应设计不同的运算方法。
抽象数据类型:
ADT,一般指由用户定义的,表示应用问题的数学模型,类似结构体
ADT 抽象数据类型名
{
数据对象:<数据对象的定义>
数据关系:<数据关系的定义>
基本操作:<基本操作的定义>
}ADT 抽象数据类型名
struct 结构体类型名:
{
结构体内容
}变量名;
(关键字struct+结构体名)组合成一种类型标识符,其地位如同通常的int、char等类型标识符
例如:struct book b1; struct book b2;
动态内存的三种开辟方式:
头文件:include<stdlib.h>申请新数组空间
malloc:(强制类型转换)malloc(开辟个数*sizeof(类型大小))
例如:int*p=(int*)malloc(3*sizeof(int))
calloc:void* calloc(unsigned int num,unsigned int size);
分配num个长度为size的连续空间,函数返回一个指向分配起始地址的指针
str = (
char
*)
calloc
(10,
sizeof
(
char
));
realloc:(地址,新空间的大小):给旧地址赋予新的空间大小,用法类似malloc
•释放老数组的空间
free(p);p=NULL;
引用参数:
在函数参数中,参数的前面可以加符号“&”修饰,表示该参数为引用参数(变参)。
在函数体内,如果对引用参数的值进行了修改,将会导致它所代表的实参值的改变
没有用“&”修饰的参数是值参。
算法的描述和算法分析
算法是指在有限的时间范围内,为解决某一问题而采取的方法和步骤的准确完整的描述。
算法特征:
有穷性、确定性、可行性、输入(可以为零)、输出
算法优劣标准:
正确性 可读性 健壮性 高效率与低存储量
分析算法:
问题规模:算法求解问题的输入量的多少
等差数列:Sn=n*a1+n(n-1)d/2或Sn=n(a1+an)/2
等比数列:
注:只需要考虑语句中执行次数最多的语句的执行次数就行
算法空间复杂度:
若一个算法的空间复杂度为O(1),则称此算法为 原地工作或就地工作算法
Class No:2线性表
线性结构特点:
存在唯一的一个被称作“第一个”的数据元素;
存在唯一的一个被称作“最后一个”的数据元素;
除第一个外,集合中的每个数据元素均只有一个前驱;
除最后一个外,集合中的每个数据元素均只有一个后继;
线性表:
线性表是n个数据元素的有限序列,可表示为: (a1, a2, ..., an)。n 个元素必须具有相同特性(同一类型)相邻元素之间存在序偶关系。
顺序表-线性表的顺序存储表示:
特点:实现逻辑上相邻—物理地址相邻 ,实现随机存取
实现:可用一维数组实现
要素:空间、长度、容量三个要素
顺序表的插入的平均次数:N/2
顺序表的删除的平均次数:(N-1)/2
顺序表的查找的平均次数:(N+1)/2
线性表的链式表示:![ec6cfe85b5664993825d88b8be30b53b.png](https://img-blog.csdnimg.cn/ec6cfe85b5664993825d88b8be30b53b.png)
头指针:链表中第一个结点的存储位置叫做头指针。若链表有头结点,则头指针就是指向链表头结点的指针
头结点:是为了操作的统一与方便而设立,放在第一个元素结点(首源节点)之前,其数据域一般无意义
空表表示:头指针为NULL
如果p表示指向某个结点的指针;
(*p)表示p所指向的结点;
(*p).data:p->data表示p指向结点的数据域;
(*p).next:p->next表示p指向结点的指针域;
关于线性表的算法:
详情请阅读作者另一篇文章
循环链表:
双向链表:
prior | data | next |
双向链表的插入:(将s插入到p之前)
- s-> prior=p->prior;
- p->prior->next=s;
- s->next=p;
- p->prior=s;
双向链表的删除:
- p->prior->next=p->next;
- p->next->prior=p->prior;
顺序表和链表的比较:
顺序表适宜于做查找这样的静态操作;
链表宜于做插入、删除这样的动态操作。 若线性表的长度变化不大,且其主要操作是查找,
存储密度: = (结点数据本身所占的存储量)/(结点 结构所占的存储总量)
Class No:3栈与队列
栈:
允许插入、删除的一端称为栈顶(top),另一端称 为栈底(bottom),不含任何数据元素的栈称为空栈。
特点:先进后出
共享栈:
注:下溢则可能是正常现象,因为栈在程序中使 用时,其初态或终态都是空栈,所以下溢常 常用来作为程序控制转移的条件。
顺序栈的操作:
链栈的操作:
栈应用:
数制转换,括号匹配,行编辑程序,表达式求值,迷宫寻路:
行编辑程序:数据结构中的一些操作_Log1c77的博客-CSDN博客
队列:
元素的删除在表的另一端进行。允许插入的一端称为队尾(rear),允许删除的一端称为(front)。
循环队列:
队头指针进1: front=(front+1) % queueArray.length
队尾指针进1: rear=(rear+1) % queueArray.length。
循环队列的判空条件:rear== front 判满:(rear+1)% MaxSize == front
队列长度:count=(rear-front+Maxsize)% MaxSize
进队操作:rear=(rear+1)% MaxSize 出队操作:front=(front+1)% MaxSize
循环队列操作:
链队列:
用链表表示的队列称为链队列,链式队列也不存在假溢出的问题,操作方法和链表相同。
队列应用:
树的层次遍历:
遍历杨辉三角形的输出 :队列实现杨辉三角(附详细图解)_Ornamrr的博客-CSDN博客_队列输出杨辉三角
模拟服务台前的排队现象问题:数据结构实验课:实验四、队列的实现及应用_superlistboy的博客-CSDN博客
Class No:4线性结构扩展
串:
是由 0 个或多个字符组成的有限序列。
注:零个字符的串称为空串,
数组的内存映象:
设二维数组Amn,按元素的下标求其地址的计算:
以“以行为主序”的分配为例:设数组的基址为LOC(a11),
每个数组元素占据L个地址单元,那么aij 的物理地址可用一线性寻址函数计算:
LOC(aij) = LOC(a11) + ( (i-1)*n + j-1 ) * L
在C语言中,数组中每一维的下界定义为0,则:
LOC(aij) = LOC(a00) + ( i*n + j ) *L
递归方法介绍:
递归算法:描述递归定义的函数或求解递归问题的过程称为递归算法。
递归缺点:执行的效率可能会很低,在设置不当的情况下极有可能陷入死循环或 者内存溢出境。
递归算法一般适用在三个场合:
- 数据的定义形式是递归的,如Fibonacci数列;
- 数据之间的逻辑关系(数据结构)是递归的,如树、图等的定义和操作;
- 某些问题虽然没有明显的递归关系或结构,但问题的解法是不断重复执行一种操作,只是问题
- 规模由大化小,直至某个原操作(基本操作)就结束,如汉诺塔问题。
广义表:
单元素:
(称为原子,用小写字母表示);di (i=1,2,...,n),
广义表:
(称为子表,用大写字母表示);如A=(a,((b,c),d),(e,(f,g,h)))
注:
表的长度,表示广义表元素的个数。当长度为0时称为空表,
深度:
广义表中,子表最大嵌套层次(括弧的重数),
表头:
非空表的第一个元素d1
表尾:
其余元素组成的表(d2,...,dn)
注:
表结点的数目与广义表的括号对数目基本一致;
Class No:5树与二叉树
树结构 :
(除了一个称为根的结点外)每个元素都有且仅有一个直接前趋,有且仅有零个或多个直接后继。
树特点:
- 树的根结点没有前驱结点,除根结点之外的所有结点有且只有一个前驱结点。
- 树中所有结点可以有零个或多个后继结点。存在空树
部分基本术语:
- 结点——表示树中的元素,包括数据项及若干指向其子树的分支;
- 结点的度——结点拥有的子树个数;
- 树的度——各个结点的度的最大值;
- 叶子(leaf)——度为0的结点;
- 森林(forest)——m(m>=0)棵互不相交的树的集合。
二叉树的定义:
二叉树是n(n>=0)个结点的有限集合,它或为空树(n=0),或由一个根结点和两棵分别被称为左子树和右子树的互不相交的二叉树构成。
二叉树的特点:
- 二叉树中每个结点最多有两棵子树;二叉树每个结点的度小于等于2;
- 左、右子树不能颠倒——有序树;
二叉树的性质:
- 性质1 :一棵非空在二叉树的第 i 层上至多有 2^i-1个结点。
- 性质2:一棵深度为k的二叉树中,最多具有2^(k-1)个结点,具有2k-1个结点的树就是满二叉树。
- 性质3:对于一棵非空的二叉树,如果叶子结点数为n0 ,度数为2的结点数为n2 ,则有: n0 = n2 +1。同时n(节点数)=n0+n1+n2;
- 性质4:具有n个结点的完全二叉树的深度k=[ log2n ]+1。
- 性质5:若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点:
(1) 节点i的左孩子结点编号为2i,右孩子结点编号为 2i+1;
(2) 若 i=1,则该结点是二叉树的根,无双亲,否则i>1,其双亲结点的编号为i/2;
(3) 若 2i>n,则该结点无左孩子,否则,编号为 2i 的结点为其左孩子结点;
(4) 若 2i+1>n,则该结点无右孩子结点,否则,编号为2i+1 的结点为其右孩子结点。 - 注:树中边的算法:e=n0*0+n1*1+n2*2+......
- e=n-1
满二叉树:
在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一层上,这样的一棵二叉树称作满二叉树。
注:高度为h的满二叉树,n=2h-1,n一定为奇数
完全二叉树:
完全二叉树是一种叶子结点只能出现在最下层和次下层且最下层的叶子结点集中在树的左边的特殊二叉树。
特点:
- 完全二叉树中如果有度为1的结点,只可能有一个,且该结点只有左孩子。
- 深度为k的完全二叉树在k-1层上一定是满二叉树。
- n为奇数时,n1=0, n为偶数时,n1=1
完全二叉树的顺序结构:
优势:结点间关系蕴含在其存储位置中;
劣势:浪费空间,仅适合于存满二叉树和完全二叉树。
完全二叉树的链式存储结构:
部分算法:
特点:
n个节点有2n个指针,n个节点中有n-1条边,空链域就是2n-(n-1)=n+1,即n+1个空指针
优点:既便于查找孩子结点,又便于查找双亲结点
缺点:相对二叉链表存储结构而言,它增加了空间开销。
二叉树的遍历:
先(前)序遍历二叉树:
(1)访问根结点A;
(2)先序遍历左子树:即按DLR的顺序遍历左子树;
(3)先序遍历右子树:即按DLR的顺序遍历右子树。
中序遍历二叉树:
(1)中序遍历左子树:即按LDR的顺序遍历左子树;
(2)访问根结点A;
(3)中序遍历右子树:即按LDR的顺序遍历右子树。
后序遍历二叉树:
(1)后序遍历左子树:即按LRD的顺序遍历左子树;
(2)后序遍历右子树:即按LRD的顺序遍历右子树;
(3)访问根结点A。
按层次遍历二叉树:
从二叉树的第一层(根结点)开始,从上至下逐层遍历,在同一层中,则按从左
到右的顺序对结点逐个访问。
各种遍历方法的算法:
确定二叉树:
- 已知前序序列和中序序列,可以唯一确定二叉树
- 已知后序序列和中序序列,可以唯一确定二叉树
线索二叉树:
为了保留结点在某种遍历序列中直接前驱和直接后继的位置信息,可以利用二叉树的二叉链表存储结构中的那些空指针域来指示。这些指针被称为线索(thread)
结构:
树的存储结构:
1、双亲表示法:
结点由数据和双亲位置构成
优势:便于查找双亲结点,结构简单,便于操作。
劣势:求孩子结点需遍历整棵树
2:孩子链表表示法:
树的结点由数据和第一个孩子结点位置构成。孩子结点由孩子结点编号和下一孩子位置构成
优势:便于查找孩子结点
劣势:找双亲结点很复杂
3:孩子兄弟(左孩子-右兄弟)表示法:
树与二叉树的转换:
二叉树与树都可用二叉链表存储,以二叉链表作中介,可导出树与二叉树之间的转换。
树转换为二叉树:
从树的根结点开始,从上到下,看每一个结点,把你正在看的结点的孩子放在左子树,兄弟放在右子树。
口诀:
1. 将 节点的孩子 放在左子树;
2. 将 节点的兄弟 放在右子树。
森林与二叉树的转换 :
树和森林的遍历:
深度优先:
先序(根)遍历(与二叉树同理)
若树不空,则先访问根结点,然后依次先序遍历各棵子树。
后序(根)遍历(与二叉树同理)
若树不空,则先依次后序遍历各棵子树,然后访问根结点。
宽度优先:
若树不空,则自上而下自左至右访问树中每个结点。
注:树的先根遍历等价于对应二叉树的前序遍历!
后根遍历等价于对应的中序遍历!
赫夫曼树:
具有最小带权路径长度的二叉树。
二叉树的路径长度:
由根结点到所有叶子结点的路径长度之和。
二叉树的带权路径长度:
- 从根结点到各个叶子结点的路径长度与相应结点权值的乘积之和。
- 权值越大的叶子结点越靠近根结点,而权值越小的叶子结点越远离根结点。
赫夫曼编码:
赫夫曼树中的左分支代表0,右分支代表1,这样从根结点到每个叶子结点所经过的路径分支组成的0和1的序列便为该结点对应字符的编码,