第一章 绪论
数据:所有能输入到计算机中并被计算机程序处理的符号的总称。
数据结构:数据间的相互关系,即数据的组织形式。包括三个方面:数据的逻辑结构,数据的存储结构,数据的运算(对数据的操作)。
逻辑结构:线性结构(2 线性表;3 栈和队列;4 数组和广义表;5 串)和非线性(6 树;7 图)
8 查找 ;9 排序
存储结构(数据的物理结构):顺序存储、链式存储、索引存储、散列存储
顺序存储和链式存储的优缺点:
顺序存储优点:存取方式是随机的,读取数据比较方便,缺点:插入和删除操作需要移动大量的数据
链式存储方式的优缺点与顺序相反。
判断算法好坏的标准:时间复杂度和空间复杂度(耗费的存储空间),合称算法复杂度。
时间复杂度:渐进时间复杂度T(n)
第二章 线性表
线性表:由n(n>=0)个相同数据类型的元素组成的有限序列。
逻辑特征:(1:1)
l 第一个结点是开始结点,无前驱有唯一的后继
l 最后一个结点是终端节点,无后继有唯一的前驱
l 中间结点,有唯一前驱和唯一的后继
逻辑结构的定义方式:(元素1,元素2,。。。)
存储结构:(物理结构)
l 顺序存储(用数组体现):预先分配一段连续的区域,静态分配 int a[10]; a表示a[0]的地址,大小是事先知道的,注意溢出问题。
顺序表:两层含义:逻辑结构上线性结构
物理上顺序存储
l 链式存储(用指针):占用的区域不一定连续,动态分配;malloc free
链表:两层含义:逻辑结构上线性结构
物理上链式存储
顺序表的插入:maxsize表示最多存放的元素的个数,size表示有效元素的个数
1、 判断满不满:满的条件:size= =maxsize
2、 插入的位置是不是合法(i>=1&&i<=size+1)
3、 后移(先移动后面的)
4、 插入 size++;
顺序表的删除:
1、 判断空间是否是空 (size= =0)
2、 判断位置是不是合法 (1<=i<=size)
3、 前移(先移动前面的)
4、 Size- -
移动次数:n/2插入,(n-1)/2删除
链表:
结点:date+next
分类:单链表、循环链表、双链表
三个术语:头结点、头指针、开始结点
插入操作:p结点的后面
1、 生成空间:s=(S *)malloc(sizeof(S));
2、 赋值:s->data=X;
3、 s->next=p->next;(修改新结点的指针)
4、 P->next=s;
链表的删除:p结点的后面的结点
1、 q=p->next;
2、 p->next=q->next; //p->next=p->next->next;
3、 free(q);
删除p结点本身:
A、 寻找p结点的前驱结点,转化为删除p结点的前驱结点的后继
q=head;
while(q->next!=p)
q=q->next;
q->next=p->next;
free(p);
B、 删除p的后继(p结点不是终端结点,p一定要有后继)
q=p->next;
p->data=q->data;
p->next=q->next;
free(q);
多项式表达式:结构体一个系数,一个指数
第三章 栈和队列
栈和队列是特殊的线性表。特殊在插入和删除操作都是在表的端点处进行。
栈Stack:操作受限(插入和删除)的线性表。
在线性表的末尾做插入和删除,叫做栈顶top 线性表的开始部分叫做栈底 bottom
特点:FILO
典型应用:函数的调用,数制转换(求余过程中,需逆序输出),括弧匹配检验,迷宫问题(方向设置为8个方向,走过标记),表达式求解问题(中缀表达式,后缀表达式),递归。
后缀表达式:加上所有能加的括号;用离右括号左边最近的运算符来替代所有的右括号;去掉所有的左括号。后缀表达式中没有括号,运算符优先级也蕴含在表达式中,可直接利用栈来求解。运算数是个位的数。中间不能出现空格。
存储方式:
l 顺序存储(用数组体现,静态分配存储空间)
l 链式存储(涉及到指针,动态分配和释放存储空间)
顺序栈的插入:top表示栈顶元素的下标,定义是int类型的,格式是int top;maxsize表示空间的大小,最多存放多少个元素。
(在顺序表的基础上修改的,红颜色粗体部分去掉)
1、 判断满不满:满的条件是top= = maxsize-1
2、 插入的位置是不是合法(i>=1&&i<=size+1):去掉原因是位置固定 size+1
3、 后移(先移动后面的):去掉
4、 top++;插入(data[top]=X)
顺序栈的删除:(在顺序表的基础上修改的,红颜色粗体部分去掉)
1、 判断空间是否是空 (top= =-1 top<0)
2、 判断位置是不是合法(1<=i<=size)
3、 前移(先移动前面的)
4、 top - -
链栈的插入操作:top表示栈顶的指针,类似于head,top是指针变量 格式:数据类型 *top;
1、 生成空间:s=malloc(。。。。);
2、 赋值:s->data=X;
3、 修改指针:s->next=top;(先修改的是新结点的指针)
4、 修改top指针:top=s;
链栈的删除:
1、 p=top;
2、 top=top->next;
3、 free(p);
进栈的顺序是123,则可能的出栈的顺序是123、132、213、231、312、321
队列:
1、 定义:操作受限的线性表(在表的一端进行插入、在另一端进行删除)
2、 在线性表的末尾进行插入操作,叫做队尾 rear,指向队尾元素的下一个位置
在线性表的开始做删除操作,叫做队头 front
3、 特点:FIFO
4、 存储结构:
l 顺序存储(用数组体现,静态分配存储空间)
l 链式存储(用指针体现,动态分配和释放存储空间)
入队列的顺序是1234,则出队的顺序是1234。
避免顺序队列的假溢出现象(存储空间未满,rear到达终点),引入循环队列。
循环队列:队列的顺序存储结构
队列空时和队列满时判断条件都相同,就少用一个元素空间,约定以“队列头指针front在队尾指针reat的下一个位置上”为队列满的标志。rear指向的是队列中空的位置。
循环队列长度是maxsize,则最多存放maxsize-1个元素。
l Front表示:队头元素的前一个位置
l Rear:队尾元素的下标
循环队列空的条件是:front= = rear
循环队列满的条件是:front==(rear+1)%maxsize
循环队列的元素的个数是:(rear-front+maxsize)%maxsize
循环队列的队头元素的下标:(front+1)%maxsize
备注:凡涉及到+1,都要%maxsize
顺序队列的插入:(在顺序表的基础上修改的,红颜色粗体部分去掉)
1、 判断满不满:满的条件:front= =(rear+1)%maxsize
2、 插入:rear=(rear+1)%maxsize
data[rear]=X;
顺序队列的删除:(在顺序表的基础上修改的,红颜色粗体部分去掉)
1、 判断空间是否是空:空的条件是front= =rear
2、 删除:front=(front+1)%maxsize
链队列:队列的链式存储结构
Front表示的队头元素的指针
Rear表示队尾的指针
链队列的插入操作:
1、 p=malloc(…);
2、 p->data=X;
3、 p->next=NULL;(先修改新产生结点的指针)
4、 rear->next=p;
5、 rear=p;
链队列的删除:(带头结点的队列)
一、 删除真正的队头元素
1、 判断队列是否为空:空的条件是front= = rear
2、 s=front->next;
3、 front->next=s->next;
4、 free(s);
二、 通过删除头结点转化为删除队头元素
1、 判断队列是否为空:空的条件是front= = rear
2、 s=front;
3、 front=front->next;
4、 free(s);
练习:
(10,20,30)
1、 线性表,画出带头结点的单链表
2、 栈,分别画出顺序存储结构 链式存储结构
3、 队列,分别画出顺序存储结构 链式存储结构
第四章 数组和广义表
数组:两种操作(查找和修改)
两种存储结构:按行优先、按列优先
因为数组没有插入和删除,所以顺序存储结构更适宜。
运算:计算地址、计算按行优先时的地址按列优先时是哪个元素
广义表:(不考)元素具有其自身结构。是递归定义的线性结构。
l 表头一定是原子。 (×)
l 表头一定是广义表。 (×)
l 表尾一定是原子。(×)
l 表尾一定是广义表。(√)
元素ai可以是原子项,可以是广义表。圆括号将广义表括起来,逗号分隔元素。
广义表的数据元素有相对次序;长度定义为最外层包含的元素个数;深度定义为所含括弧的重数,原子项深度为0,空表深度为1.;广义表可以共享;可以是一个递归的表,递归表的深度是无穷值,长度时有限值;任何一个非空广义表可分解为表头和表尾。
第五章 串
两个串相等的充要条件是:串长相等并且对应位置上的字符要相同。
空串和空白串区别:n为0,空白串是指内容是空格。串的结束标志:“”和字符数组。
串的运算:串的链接、求子串、求子串在主串中的位置(模式匹配)、串的比较、串的复制、串的替换 等等。
Int a[]=”123”; (√)
Int a[10]; a=”123”; (×)
Char name[10]=”gmm”;
For(i=0;i<size;i++)
If(strcmp(name,stu[i].name)= =0)
练习:
输出所有的“水仙花数”。所谓“水仙花数”是指一个3位数,其各位数字立方和等于该数本身。例如:153是一个水仙花数,应为153=13+53+33。
第六章 树
树:非线性结构
1、定义:有n(n>=0)个结点组成的有限集合。对于非空树来说:有且仅有一个根结点;子树由t1,t2.。。互斥的集合。
2、术语:结点的度、树的度、树的高度、路径、路径的长度、树的路径长度、树的带权路径长度
结点的度:结点拥有的子树的个数,最大的结点的度称为树的度。
结点的层次:根节点层次为1。
树的高度或深度:树中结点的最大层数。
路径长度:路径所经过的边的数目。
森林:m(m>=0)棵互不相交的树所构成的集合。
3、树的存储:双亲表示法、孩子表示法、双亲孩子表示法、孩子兄弟表示法(顺序,链式,数组链式)
二叉树:
判断:二叉树是树;是度为2的树;是特殊的树;是度为2 的有序树。(×)二叉树不一定是有序的。
5个性质:
性质1:二叉树第i层上最多2i-1(数学归纳法)
性质2:深度为k的二叉树上最多2k-1(等比数列),满二叉树的结点数量为2k-1。
性质3:二叉树上度为0的节点有n0个,度为2的节点有n2个,则n0=n2+1 (N=n0+n1+n2=0*n0+1*n1+2*n2+1)
满二叉树完全二叉树
l 满二叉树一定是完全二叉树。(√)
l 完全二叉树一定是满二叉树。(×)
l 完全二叉树的结点如果有左孩子,则它一定有右孩子。(×)
l 完全二叉树的结点如果有右孩子,则它一定有左孩子。(√)
l 完全二叉树的结点如果没有左孩子,则它一定没有右孩子,该结点一定是叶结点。(√)
完全二叉树的定义:一棵具有n个结点的二叉树,它的结构与一棵满二叉树的前n个结点的结构相同。满二叉树:所有叶子结点都在统一层上,所有的非叶子结点的度都为2.
性质4:有n个节点的完全二叉树的深度为(设深度k,2k-1-1+1≤n≤2k-1)[log2(n+1)]+1
[]表示取整。
性质5:把完全二叉树编码,从上到下,同一层上从左向右,
l I=1:是根结点,没有双亲;i>1:有双亲,双亲编码是i/2;
l I:2i是左孩子,如果存在右孩子,则右孩子的编码是2i+1;
l I:2i+1是它的右孩子,左孩子的编码是2i。
练习:
1、有1001个结点的完全二叉树,树的高度为(),其中叶子结点的个数为(),度为1的结点的个数为(),度为2的结点的个数为()。
2、已知二叉树有52个叶子结点,度为1的结点个数为30则总结点个数为()。
二叉树的存储:
l 顺序存储---à完全二叉树的存储(下表为0的空间没用 性质5来判断双亲和孩子的关系)
l 链式存储(二叉链表、带双亲的二叉链表(三叉链表))-à一般二叉树
二叉树的遍历:先序遍历(第一个节点是根结点)、中序遍历(来判断左右子树的结点)、后序遍历(最后一个节点是根结点)
根据先序和中序、中序和后序得到二叉树。
层次遍历:从二叉树的第一层开始,上到下,左到右的遍历。
练习:
1、已知二叉树的中序遍历序列是ACBDGHFE,后序遍历序列是ABDCFHEG,请构造一棵二叉树。
2、已知二叉树的层次遍历序列为ABCDEFGHIJK,中序序列为DBGEHJACIKF,请构造一棵二叉树。
3、已知二叉树的前序、中序和后序遍历序列如下,其中有一些看不清的字母用*表示,请先填写*处的字母,再构造一棵符合条件的二叉树。
(1)前序遍历序列是:*BC***G*
(2)中序遍历序列是:CB*EAGH*
(3)后序遍历序列是:*EDB**FA
线索二叉树:将二叉树的结点的空闲指针指向该结点在某种遍历次序下的前驱结点或后继结点所形成的树的结构。分为前序线索二叉树,中序。。,后续。。。
树、森林和二叉树之间的转换:
l 树----à二叉树:加线(相邻兄弟加虚)、抹线(删除双亲与非第一个孩子连线,即删除同层中相连的结点靠右结点与上一层结点的连线)、调整(结论:转换后的二叉树没有右子树)
l 二叉树-----à树:加线(相邻兄弟加虚)、抹线(删除双亲与非第一个孩子连线,即删除同层次中结点相连的线)、调整(剩余线中有虚线的变实线,调整角度)
l 森林-----à二叉树:
l 二叉树-----à森林:串接,按先后顺序,依次将后边的二叉树作为前边二叉树的根节点的右子树。
树、森林和二叉树的遍历
1、树的先跟遍历次序与它所对应的二叉树的先序遍历次序相同;
2、树的后跟遍历次序与它所对应的二叉树的中序遍历次序相同;
3、森林的先序遍历次序与它所对应的二叉树的先序遍历次序相同;
4、森林的中序遍历次序与它所对应的二叉树的中序遍历次序相同;
哈弗曼树及其编码:
带权二叉树,权值为w。
l 术语:路径、路径的长度、树的路径长度(从根结点到每一个叶子结点的路径长度之和)、带权路径长度(根结点到叶子结点的路径长度*权值乘积之和)
l 构造:每次找两个权值最小的结点进行合并,得到的新节点的权值放到最后,循环合并,直到只剩下一个结点的时候结束过程,这时候的二叉树就是所求的最优二叉树(哈夫曼树)。
l 哈弗曼编码:左分支为0,右分支为1,从根结点到叶子结点所经过的边的编码就是它所对应的哈弗曼编码。
第七章 图
1、定义:
l 图:Graph 两个集合V(vertex) E
l 有向图:V E <V,W > 边又称为弧,<vi,vj>,弧尾(vi不带箭头的一端),弧头(带箭头的一端)
l 无向图: (V,W)= =(W,V)边(vi,vj)
2、术语:
l 完全图(任意两个定点之间都有边 无向图 n*(n-1)/2 有向图 n*(n-1))
l 结点的度:与这个顶点相关联的边的条数TD
l 入度:针对的有向图 以这个顶点为弧头 ID
l 出度:针对有向图 以这个顶点为弧尾OD, TD=ID+OD
l 边的个数:所有顶点的度的总和/2
l 连通图:
如果两个顶点之间有路径(顶点序列),那么这两个顶点就是连通的
连通图:无向图 任意两个顶点之间都有路径
判断: 完全图一定是连通图(√)
连通图一定是完全图(×)
l 连通分量:非连通图的极大连通子图
l 强连通图:有向图中两个顶点间相互存在路径的图
强连通分量:有向图的极大强连通子图
l 生成树:连通图的生成树是连通图,有n个顶点,(至少需要n-1边使得它连通,)包含且仅包含n-1条边,一个连通图的极小连通子图
3、 图的存储:
(1)邻接矩阵:
如果有n个顶点的无向图,存储空间压缩 n*(n+1)/2
如果有n个顶点的有向图,存储空间不能压缩 n*n
有向图的连接矩阵 第i行非零元素的个数------à出度
第i列非零元素的个数------à入度
无向图的连接矩阵:第i行(或者列)非零元素的个数------à度
(2)邻接表:
无向图中顶点Vi的度是邻接表中结点的个数;
有向图中,顶点Vi的出度是邻接表中结点的个数;
顶点Vi的入度是逆邻接表中结点的个数。
图的遍历:深度遍历广度遍历
深度遍历:v出发,访问邻接点进行深度优先遍历,直到所有和v有路径相通的点被访问,再另选未被遍历的点,进行深度优先遍历。类似先根遍历。0-1-3-7-4-5-2-6
广度遍历:访问v的未曾访问过的邻接点,再以邻接点为出发点访问其邻接点,使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”。类似层次遍历。0-1-2-3-4-5-6-7
生成树:深度遍历的生成树 广度遍历生成树
最小生成树:
l Prim:把顶点分为两个集合,分别从这两个集合中各找出一个顶点,使得这两个顶点之间的边的权值是最小的,把绿颜色顶点合并到黄颜色中,继续。。。,直到所有顶点都合并到黄颜色中为止。
l Kruscal:把顶点分别作为集合,每次从边的集合中查找权值最小的两个顶点:如果这两个顶点分属于不同集合,合并;如果这两个顶点分属于同一集合,继续查找下一条权值较小的边。
最短路径:迪杰斯特拉算法
l 第一条最短路径:在所有的直达路径中,找出最短的一条路径
l 在后面的最短路径:要么直达;要么经过已经求出的最短路径的顶点。
拓扑结构:主要用于查看工序之间的前驱后继的关系。AOV网
第八章 查找
1、线性表的查找(静态查找)
l 顺序查找:顺序表 链表 n
l 折半查找(二分查找):有序的顺序表(中间元素作为比较对象):递归和非递归
l 索引顺序表查找(分块查找):结合了顺序查找和折半查找,将查找表分成若干分块,建立相应的索引,索引表是按序的。子表中的数据均小于关键字的值。
2、树表的查找(动态查找):二叉排序树(二叉查找树)
(1)定义:可以是空树,如果非空,满足3个条件(左不为空,左上的所有点均小于根,右不为空,均大于根;左右子树也都是二叉查找树)
(2)查找
(3)插入(作为叶子结点插入)
(4)删除(3种情况):
l 删除叶子结点:直接删除,修改其双亲的左指针或者右指针为NULL
l 删除只有左子树或者只有右子树:修改其双亲直接指向其左孩子或者右孩子
l 删除既有左子树又有右子树:用中序遍历的前驱结点代替删除结点,然后删除前驱结点。
平衡二叉树:AVL空二叉树或是具有下列性质:左子树和右子树都是平衡二叉树,且左和右子树高度差得绝对值不超过1.
哈希表:选取某个函数,根据寒素按关键字计算数据的存储位置,按次位置存放数据(哈希造表);查找时,同一个函数对给定值x计算地址,再将x与地址中的关键字比较,哈希方法,散列法。