定义:具有相同类型数据的n个元素的序列
线性表是一种逻辑结构,包含两种存储方式:顺序存储和链式存储。
:逻辑上相邻的两个元素,物理位置上也相邻,插入删除需要移动大量元素。
时间复杂度:插入,删除的时间复杂度是O(n)
查找时间复杂度:按值查找是O(n),按照位置查找是O(1),顺序存储有着随机存取的性质:随机存取就是可以按照地址访问数据
:有着单链表,循环双链表,循环单链表,静态链表。
链表的几种判空:
1.没有头结点的链表判空 head==null
2.有头结点的判空head->next==null
3.循环单链表的判空:L->next==null
4.循环双链表的判空:L->next==null&&L->prior==null
链式存储的插入,删除时间复杂度还需要因地制宜,例如头插法时就是O(1),中间插入就是O(n),
3.两种可以记忆一下的时间复杂度
有序单链表的生成:O(nlog2^n),,,有序顺序表的按值查找O(log2 ^n)
4.静态链表:预先分配一块连续的存储空间,适合数据元素固定不变的场景:比如操作系统的文件分配表FAT,静态链表以next==-1作为结束标志,插入删除和动态链表相同。
栈:只允许一端进行插入删除操作的线性表(栈顶),卡特兰数:n个元素进栈后出栈元素不同排列的个数C(2n,n)/n+1
队列:操作受限的一种表,只允许在表的一端插入,一端删除,插入数据的一端为队尾,删除数据的一端为队首
1.栈的顺序存储:
栈的初始化:S.top=-1;
栈的判空:S.top==-1;
栈的入栈:S.data[++S.top]=X;指针加一再入栈
栈的出栈:X=S.data[S.top--];先出栈,指针再减一
判断栈满:S.top==Maxsiza-1;
栈的长度:S.top+1
若栈顶指针S.top=0;则栈顶指针指向栈顶元素的下一个位置,此时入栈为S.data[S.top++]=X;出栈为X=S.data[--S.top]
2.共享栈
利用栈底位置相对不变的特性,两个顺序栈共用一个空间,栈底设置在共享空间的两端,栈顶向共享空间的中间延伸。
判断栈空:stack1:S.top==-1, stack2:S.top==MAXSIZE;
判断栈满:top2-top1=1;
进栈时,栈1是指针加一再赋值,栈二是指针减一再赋值,出栈则刚好相反。
共享栈的目的:节省存储空间,防止上溢。
3.栈的链式存储
采用单链表的形式存储,所有操作都在链头进行,便于多个栈共享存储空间,更好的利用空间资源,避免上溢。
链栈插入结点的操作:x->next=top,top=x;
总结:栈,不管顺序栈还是链栈,入栈都是指针加一再赋值
1.队列的顺序存储
分配一块连续的存储空间,并且配上两个指针,队首指针front指向队头,队尾指针rear指向队尾的下一个位置
出队:队不空时:取队首元素,队首指针加一
入队:队不满:送值到队尾,队尾指针加一
队列初始化:Q.rear=Q.front=0;
判空:Q.rear==Q.front==0;
判满:由于无法判满,引出循环队列
2.循环队列(顺序存储结构)
初始化:Q.rear=Q.front=0;
入列:(Q.rear+1)%MAXSIZE
出列:(Q.front+1)%MAXSIZE
队列空的条件:Q.rear==Q.front
判断队满:
1.牺牲一个存储单元:队尾指针的下一个位置是队首指针,此时队满
(Q.rear+1+MAXSIZE)%MAXSIZE=Q.front
队空的条件:Q.rear==Q.front
队长:(Q.rear-Q.front+MAXSIZE)%MAXSIZE
2.增设表示数据元素个数的数据成员Q.size
队空:Q.size==0;
队满:Q.size==MAXSIZE;
3.增设数据成员tag
当Q.rear==Q.front时,tag==0,队空,tag==1时,队满
3.队列的链式存储
带有队首指针和队尾指针的单链表
判空:Q.rear==Q.front==NULL
双端队列:两端同时可以插入和删除的队列
输入受限:某一端只能删除不能输入的双端队列
输出受限:某一端只能插入不能删除的双端队列
最适合链队的链表:带首尾指针的非循环单链表
4.队列,栈和数组的应用
一:栈的应用
1.括号匹配:
遇到左括号入栈,遇到右括号出栈,匹配后栈非空,则匹配失败
2.表达式求值(前缀表达式,中缀表达式,后缀表达式)
前缀表达式:+ab 中缀表达式:a+b 后缀表达式:ab+
中缀表达式转后缀表达式:
1.确定好顺序,表达式中有括号第一顺序,括号外乘除优先加减
2.选择一个运算符,按照(左操作数,右操作数,运算符的顺序),形成一个新的操作数
例子:
中缀:a+b*(c-d)-e/f 转成的后缀: abcd-*+ef/-
确定好运算顺序,这里的运算顺序是
(c-d)-->b*(c-d)-->a+b*(c-d)-->a+b*(c-d)-e/f
这里以c-d为第一顺序,所以以它分左右,ef在左边运算符的右边
**中缀转前缀表达式:**和转后缀一样的方法,不过此时是按照(运算符 ,左操作数,右操作数)的方式
前缀表达式的机算:
1.从右往左扫描,遇到操作数压入栈内,遇到运算符,弹出栈的两个操作数,得出的结果压入栈顶
后缀表达式的机算:
1.从左往右扫描:遇到操作数压入栈内,遇到运算符,弹出栈的两个操作数,得到的结果压入栈顶
中缀表达式的机算:
1.初始化两个栈
2.从左到右扫描,操作数入操作数栈,运算符和界限符进入另一个栈
3.运算符若是优先级高于栈顶运算符或空栈,则压入栈顶,如果低于栈顶运算符,则弹出两个操作符,用当前运算符栈顶元素运算,运算结果入栈顶,若是同级,比如加减,默认栈顶高半级。遇到界限符(,(继续入栈,遇到),则开始弹出栈顶两个元素,运算,知道遇到(,结束。
中缀表达式机算看起来复杂,但是一般选择题出现,自己列一个表达式,带入选项给的方法就行,不用死记。
二:队列的应用
1.解决主机与外设速度不匹配的问题:打印机
2.解决多用户引发资源竞争的问题:先进先出cpu分配给用户使用
3.图的广度优先遍历,二叉树的层次遍历:队列作为辅助空间(先序,中序,后序用栈实现)
三:数组的应用
1.多维数组的存储
行优先,一行一行存储;列优先:一列一列存储,,,这个对应位置的公式当场推就行
2.特殊矩阵的压缩存储:
对相同的元素分配一个存储空间,对零元素不分配存储空间,这是为了节省空间,常见的特殊矩阵有对称矩阵,三角矩阵,三对角矩阵,稀疏矩阵
元素下标计算公式的核心:等差数列前n项和公式:n*(首项+末项)/2
对称矩阵:关于左对角线对称的矩阵
k=i*(i-1)/2+j-1 ;i>=j(下三角区域和对角线元素)
k=j*(j-1)/2+i-1;j>=i(上三角区域)
三角矩阵:下三角矩阵(上三角区域所有元素均为同一常量)
k=i*(i-1)/2+j-1; i>=j(下三角区和主对角线元素)
k=n(n+1)/2 ;ij(下三角区元素)
三对角矩阵:在一维数组B中存放的下标为K=2i+j-3;
稀疏数组:存储方式:三元组 ,将非零元素及其相应的行和列构成一个三元组(行标,列标,值)
对称矩阵,三角矩阵,三对角矩阵三种压缩后仍然有着随机存取的特性,稀疏矩阵压缩存储后便失去了随机存取的特性
记住两个就行(其他现场推就行,记得根据题目情况看一般数组第一个位置是0):
1.对于三角矩阵,对角线一边全是一个数的记得存储一次,位置k=n*(n+1)/2
2.三对角矩阵:除了三条对角线,其余为零,这个时候位置k=2i+j-3
稀疏矩阵:非零元素比较多,采用三元组进行存储**(行标,列标,值)**表示。
压缩后,稀疏矩阵失去随机存取的特性,其他三种压缩还是有对应特性的。
简单的模式串匹配:会导致指针回溯,增加时间的开销
改进的模式串匹配:KPM算法
改进的算法中子串和模式串不匹配时,主指针不会回溯,主指针跳到next[j],继续匹配。
next[j]=数组S(由1到j-1个字符组成的串)的最长相等前后缀长度+1;
特殊:next[1]=0;以上串在数组的位置是由1开始,若是从0开始,则不用整体加一,此时next[1]=-1
1.树是一种逻辑结构,也是一种分层结构,树的根节点没有前驱,除根节点外的节点有一个前驱,
所有结点的后继可以是N个
2.结点和树的度:结点的度是结点的孩子树,最大的结点的度为树的度
3.叶子结点:度为0的结点
4.结点的层次,深度,高度:
层次=深度:从根节点开始累加 高度:叶结点开始累加
5.树的高度:树中结点的最大层次
6.有序树,无序树:树中结点各子树从左到右是否有次序
7.路径和长度:路径:两节点所经过的结点序列,路径长度:经过的边的个数
8.森林:树的根节点删去就成了森林
m叉树和度为m的树:
m叉树:任何子树的结点<=m,可以全部小于m,可以为空树
度为m的树:至少有一个节点的度等于M,不可能是空树,树的结点个数最少是m+1
由m叉树引出的问题:
高为h的m叉树最多的结点树:(m^h-1)/m-1 利用等比数列求和
具有n个结点的m叉树的最小高度:利用上面的最多结点做算式得出
9.树的路径长度:根节点到每一个叶子结点的路径长度之和
10.结点度之和=分支(边数)=结点数-1
1.一些定义
二叉树是有序树,有左右之分,次序不能任意颠倒,每个结点最多两个子树,二叉树可以为空树
特殊的二叉树:
满二叉树:都有左右儿子,高为h的树结点数是 2 ^h-1,父节点为i,左儿子为2i,右孩子为2i+1
完全二叉树:1到n个结点的排列和满二叉树一样,结点度为1时,只有左儿子,所以当n为奇数时,所有非叶子结点都有左右儿子结点,当n为偶数时,最大分支结点(n/2)只有左孩子,没有右孩子。i<=n/2时为分支结点,否则为叶结点。
二叉排序树:左子树任一结点一定小于右子树任一结点。
二叉平衡树:任一结点左右子树深度不超过1。
2.二叉树的链式和顺序存储
顺序存储:从上到下,从左到右,依次存入一维数组中,为了满足随机存取的特性,即使没有的空儿子,也只能添加空结点,比较浪费存储空间,比较适合满二叉树和完全二叉树。
链式存储:为了减少顺序存储带来的空间浪费
一个结点带有两个指针,其中n个结点的非空指针树=分支树=n-1,空指针树=n+1=2*结点数-分支数
1.二叉树的先序,中序,后序遍历由栈实现,层次遍历由队列实现
由遍历序列构造二叉树:必须有中序遍历
三种方式:先序中序 后序中序 层次中序,这三种可以确定一颗唯一的二叉树
2.线索二叉树
每一个结点除了第一个和最后一个,他们都有直接前驱和直接后继,规定若无左子数,Ichild作为线索指向前驱结点,若无右子树,rchild作为线索指向后继
二叉树的线索化:就是把二叉树链表的空指针指向前驱或后继,前驱和后继只有遍历才知道,实际上就是二叉树的一次遍历。
直接找到前驱或后继:在普通的二叉树中,要找到一个节点的前驱和后继节点,需要通过遍历整个二叉树来查找。而在线索二叉树中,通过添加线索,可以直接在常数时间内找到一个节点的前驱和后继节点,不需要从根节点遍历,当父节点为前驱或后继时,需要从根节点开始遍历,左右孩子做前驱后继不用从根节点遍历。
1.先序的前驱(有左右孩子,没有线索指针)为父节点,需要从根结点遍历。
2.后序的后驱(有左右孩子,没有办法通过线索指针快速找后继)为父节点,需要从根节点遍历。
3.以上两种情况采用三叉链表结构,分配一个指针指向父节点,那么也可以直接找到前驱或后继。
这里理解这个前驱和后继就要先学会构造相应的二叉树再具体分析。
中序线索二叉树:
中序序列:a+b*c-d-e/f
树中所有叶子结点的右链是线索,则右链直接指向了结点的后继;b结点的后继为 * 。
非叶子结点右链是指针,无法得到后继信息。
根据中序遍历的规律知:
结点的后继:若右标志为1,右链作为线索,否则,遍历其右子树时访问的第一个结点,即右子树中最左下的结点;
结点的前驱:若没有左孩子其左标志为 1 ,则左链为线索,指向其前驱 ;否则,遍历左子树时最后一个访问的结点(左子树中最右下的结点)位其前驱。
其实除了根节点外,左右孩子都在的情况,左右孩子分别为前驱和后继,但是为了统一点,就用上面两句话。
先序二叉线索树:
根据先序遍历的规律知:
结点的后继:若有左孩子,则左孩子就是其后继,若没有左孩子但有右孩子,则右孩子就是其后继,若是叶子结点右标志为1,则右链为线索,指向其后继;
结点的前驱:若没有左孩子其左标志为 1 ,则左链为线索,指向其前驱 ;否则,有左孩子(此时左孩子指向后继),无法存储左链,不能直接找到其前驱结点。
在先序遍历中,某一个结点的左右子树只可能是它的后继,均不可能是它的前驱,所以,如果要求其前驱,有两种方法:
1 可以使用最原始的方式从根节点依次遍历,但是浪费时间,时间复杂度
2. 采取三叉链表的数据结构,分配一个指针,用于指向该结点的父结点。
后序线索二叉树:
根据后序遍历的规律知:
结点的前驱:
1.若没有左孩子其左标志为 1 ,则左链为线索,指向其前驱 ;
2.只有左孩子,左孩子为其前驱结点;
3.左右孩子都在右孩子为前驱。
结点的后继:
后序遍历中,某一个结点的左右子树只可能是它的前驱,均不可能是它的后继,所以,如果要求其后继,有两种方法:
1 使用最原始的方式从根节点依次遍历
3. 或者采取三叉链表的数据结构,分配一个指针,用于指向该结点的父结点。
3.三种遍历的动态演示
一般会考是否存在m到n的路径:只有后序可以找到路径,所谓找到路径,就是在辅助栈中找到m到n的路径
以上图为例:
1、先序:
进入m:
m入栈,m出栈并输出;
进入m的左子树a:
a入栈,a出栈并输出;
a为叶子节点,左右孩子均为空;
回退至m;
进入m的右子树b
b入栈,b出栈并输出;
进入b的左子树n:
n入栈,n出栈并输出;
n为叶子节点,左右孩子均为空;
回退至
进入b的右子树c:
c入栈,c出栈并输出;
c为叶子节点,左右孩子均为空;
回退至b;
回退至m;
结论:栈内不存在路径,栈中一直只有一个元素
2.后序
进入m:
m入栈(栈内情况:m);
进入m的左子树a:
a入栈(栈内情况:ma);
a为叶子节点,左右孩子均为空,a出栈并输出(栈内情况:m);
回退至m;
进入m的右子树b:
b入栈(栈内情况:mb);
进入b的左子树n:
n入栈(栈内情况:mbn);//栈内出现了从m到n的路径
n为叶子节点,左右孩子均为空,n出栈并输出(栈内情况:mb);
回退至b;
进入b的右子树c:
c入栈(栈内情况:mbc);
c为叶子节点,左右孩子均为空,c出栈并输出(栈内情况:mb);
回退至b;
b的左右子树均遍历完毕,b出栈并输出(栈内情况:m);
回退至m;
结论:辅助栈内存在路径:ma mbn mbc
1.树的三种存储结构
1.双亲表示法:找父节点方便,找孩子不方便,一组连续的空间,分配一个指针指向父节点
2.孩子表示法:找孩子方便,找父节点不方便,每个结点的孩子用单链表串起来
3.孩子兄弟表示法:找孩子方便,找父节点不方便,二叉链表存储,两个指针,一个指向孩子,一个指向兄弟,这样转二叉树方便。
2.树,森林,二叉树的相互转化
1.树转化为二叉树(左孩子右兄弟)
只保留第一个孩子,孩子之间加直线,断开除第一个孩子的其他孩子的线
2.森林转化为二叉树(左孩子右兄弟,此时喊兄弟树)
森林中的每棵树转化为相应的二叉树,然后每棵树的根节点连线
3.二叉树转化为森林
左右子树非空情况,左子树看成一颗二叉树,右子树看作除左子树外的森林转化的二叉树
3.树和森林的遍历和二叉树遍历对应的关系
1.并查集
我们通常用树的双亲表示法,作为并查集的存储结构。
定义:
并查集是一种不相交集合的数据结构,用于处理不相交集合。
并查集顾名思义,合并两个集合简称“并”,找出包含给定元素的唯一集合简称“查”,并查集是表示一组不相交的集合的数据结构,简称“集”,表示对象是一组不相交的集合构成的一个集族,合起来就是“并查集”。
为了标识每个集合,从每个集合的所有元素中选出一个代表。每个集合有且仅有一个代表。类比每个小组有一个小组长。
操作
并查集主要有三种操作,即创建集合,合并集合,查找集合。
下面两个优化都可以有效减缓树高的增加,使得find的时间复杂度下降。
并的优化:小树合并到大树
查的优化:找到根结点,将查找路径的所有结点挂到根结点上,
例题:
2.并查集中,查的最坏情况就是O(n),此时树的结点都只有一个孩子。
2.二叉排序树
特性:左子树的值<根节点<右子树
二叉排序树的查找:根据他的大小特性一排一排比较得来
二叉排序树的的插入:添加新的叶结点,要根据特性添加
二叉排序树的删除:三种情况
1.无子结点:直接删除
2.单子结点:子节点代替原结点
3.左右双结点:选择左子树最大的或者右子树最小的代替原结点
3.平衡二叉树
1.特性:在插入和删除结点时,保证左右子树的高度相差不超过一,这样的树称为平衡二叉树
2.平衡二叉树:插入后的平衡因子的绝对值小于1(四种情况)
1.LL:右单旋转
2.RR:左单旋转
3.LR:先左旋后右旋
4.RL:先右旋后左旋
(左旋右旋一一对应,像这种LR从后面®先对应)
LL:左孩子的左子树插入,单旋一次以孩子作为旋转点
LR:左孩子的右子树插入,左旋加右旋两次,以左孩子的右孩子,,就是孙子级别为旋转点
3.平衡二叉树的平均查找长度:Olog(n)
4.哈夫曼树和哈夫曼编码
1.定义:哈夫曼树:也称最优二叉树,n个带权叶结点的二叉树中带权路径(WPL)最小的二叉树
结点带权路径长度:根节点到该叶结点的路径长度和该点带权值的乘积
树的带权路径长度:所有结点带权路径长度之和
构造:每次合成后存在的结点中(此时合成后的点作为新的结点参加挑选)选择两个最小的进行构造二叉树
唯一性:构造的二叉树的可以不唯一,但是他们的WPL值是唯一确定的
2.哈夫曼编码:
对编码了解
定长编码:每一个字符的编码的定长的
不定长编码:对于常用的字符用短编码,对于不常用的用长编码,这样降低平均编码长度
哈夫曼编码:被广泛应用的数据压缩码,根据出现频率构造的不定长编码
以哈夫曼树为基础的编码规则
以每个字符出现的次数作为他的权值来构造一个哈夫曼树,左边分支为0,右边分支为1,从根节点到改叶结点的路径的编码为该字符的哈夫曼编码
哈夫曼编码:叶子结点数为n,非叶子结点数为n-1
1.图的基本概念 2.图的存储和基本操作 三十字链表法 举个例子: 四:多重邻接表法 总结:prim和kruskal都是贪心算法(贪心算法,又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择 。prim和kruskal的贪心策略不同。 二:最短路径 Floyd算法: 1.查找的基本概念:在数据集合中寻找满足某种条件的数据元素的过程为查找 查找表操作:查询某个特定元素是否在查询表中,查询成功插入,查询失败时删除 平均查找长度是算法效率的最主要指标 2.顺序查找和折半查找
1.定义:边表E,顶点表V,记为G=(V,E)
线性表可以是空表,树可以是空树,图不可以是空图,边表可以为空,顶点表不能为空。
2.有向图,无向图,边是否有向
3.简单图:没有顶点到自身的边,,多重图:有自己到自己,一点到另一点不止一条边
4.完全图:也叫简单完全图
对于无向图:每个顶点之间都有边
对于有向图:每个顶点之间有存在相反方向的两条弧
连通图:无向图 强联通图:有向图 -----》带个强字有向
强连通分量:有向非强连通图的最大连通子图
连通分量:无向图的极大连通子图
生成子树:无向连通图的极小连通子图(包含全部顶点),加一条边成为环,减少一条边不连通
强连通和连通不一样,强连通是有向图,连通是无向图
连通图边数最少:n-1 一颗树 强连通图边数最少:n 一个环
5.稀疏图和稠密图
E
有四种存储:领接表,邻接矩阵,十字链表,邻接多重链表
一:邻接矩阵
就是矩阵的形式存储,有边就是1,,无边为0,适合稠密图的存储
二:邻接表(顶点表和边表)
邻接表法:
无向图:相关联的边都是
有向图:出度,仅仅是做弧尾的时候,
逆邻接表(有向图):入度:顶点作为弧头
主要知道弧结点和顶点结点,每个弧对应着一个弧结点,每个顶点对应一个顶点结点
顶点结点:
弧结点:
总结:其实和邻接表法差不多,只不过邻接表的顶点结点指向顶点结点,这里是指向弧结点,然后对相应的指针域连线就行
总结:邻接矩阵,邻接表 有向图,无向图都可以
十字链表有向图 邻接多重表 无向图
3.图的遍历
一:广度优先搜索(BFS)
类似二叉树的层次遍历,利用队列实现搜索
二:深度优先搜索(DFS)
类似于二叉树的先序搜索,l利用栈实现
总结:广度优先搜索后会生成广度优先树,深度优先搜索后会形成树(连通图)或者森林(非连通图),,,因为邻接矩阵表示树具有唯一性,所以基于邻接矩阵遍历得到的DFS序列和BFS序列具有唯一性,基于邻接表遍历得到的BFS序列和DFS序列不是唯一的
4.图的应用
最小生成树,最短路径,拓扑排序,逆拓扑排序
一:最小生成树
1.prim算法:
从一个点开始,每次找该点集合里面所有相连的边中最短的边,加入边集合,直到所有的点连通,构成最小生成树
2.kruskal算法:
1.将图中所有边对象(边长、两端点)依次加入集合(优先队列)q1中。初始所有点相互独立。
2.取出集合(优先队列)q1最小边,判断边的两点是否联通(分为直接和间接)。
3.如果联通说明两个点已经有其它边将两点联通了,跳过,如果不连通,则使用union(并查集合并)将两个顶点合并,这条边被使用(可以储存或者计算数值)。
4.重复2,3操作直到集合(优先队列)q1为空。此时被选择的边构成最小生成树。
prim适合稠密图,kruskal适合稀疏图。Prim算法适合边稠密图,时间复杂度O(n²),Kruskal算法与边有关,适合于稀疏图O(eloge),kruskal是先把边的权值排序一次再进行选择。
Dijksra,Floyd
Dijkstra算法:
从dis数组(原点到其他各个顶点当前的最短路径)选择最小值,则该值就是原点s到该值对应的顶点的最短路径,并且把该点加入到T中,此时完成一个顶点。
然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值。
然后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。
各个顶点之间的最短距离,暴力破解法,通过循环的方式,时间复杂度比较高,对于稀疏图将会生成稀疏矩阵,极大浪费了储存空间。
初始化矩阵,将所有相邻的节点对应的权值写入矩阵,不相邻的节点对应的权值初始化为inf,如下面的例子所示:
初始化结束后,开始循环,每层循环从第一个节点开始遍历,直至遍历到第n个节点,则以节点i为中介点,以节点j为起点,节点k为目标点,判断由起点j经由中介点i到达目标点k的代价值是否小于由起点j直接到目标点k的代价值,若小于,则将从起点j到目标点k的代价值d[j][k]更新为d[j][i]+d[i][k]。循环结束后,路径规划结束。每个顶点都做一次中间点,当所有结点循环完以后,算法结束。
① 以A为中介点,分别更新B/C/D/E/F/G经中介点A到其他节点的累积权值
三:拓扑排序
1.有向无环图:DAG图
2.AOV网,若DAG图表示一个工程,有向边
3.拓扑排序的定义:
每个顶点只出现一次,若顶点A在顶点B前面,则不存在顶点B到A的路径
4.拓扑排序的实现方法:
从AOV网删除一个没有前驱的顶点和以它为起点的有向边
因为后继不一定唯一,环不存在拓扑结构:因为拓扑排序的定义就是从AOV网选择一个没有前驱的顶点输出
四:逆拓扑排序
1.选择一个没有后继的结点输出,并且删除以它为终点的有向边
五:关键路径
核心思路:从汇点按照逆拓扑排序求其余顶点最迟发生时间
1.汇点与源点:汇点就是终止点
2.过程
从最末端的活动开始:
step1:将最末端的活动时间标记为0(这里选汇点,将汇点当做一个虚活动),活动a10与0(我们记汇点的活动时间为0)相加,标注在它的前置活动a7上方,同理,活动a11与0相加,标注在它的前置活动上方(这里a11有两个前置活动,都要标注):
依次选最大往前加,标注在前一个活动上方(核心):
只有关键路径的时间减少,工期才减少,当关键路径减少到一定程度的时候,可能变成非关键路径。
判断一个有向图是否有回路:
(1)深度优先 (2)拓扑排序 (3)关键路径六:查找
查找表:查找表是一种存储结构,专门用来存储无逻辑关系的数据。也可以这样理解,查找表就是一个包含众多元素的集合,表中的各个元素独立存在,之间没有任何关系。
对于无逻辑关系的数据,做的最多的操作就是从中查找某个特定的元素。和有逻辑关系的数据相比,在无逻辑关系的数据中查找特定元素的难度更大。例如,同样是查找一个电话号,在杂乱无章的电话簿中查找既费时又费力,在有序的电话簿中很快就能找到。
原本没有逻辑关系的数据,为了提高查找效率,会人为地给数据赋予一种逻辑关系,继而选用线性表、树或者图结构来存储数据。比如说,为电话簿中的电话号赋予“一对一”的关系,就可以用线性表(顺序表或者单链表)存储电话号。
从名称上看,查找表是一种新的存储结构,但实际上它指的就是用线性表、树或者图结构来存储数据,只不过数据间的逻辑关系是人为赋予的。
数据结构中,将存储无逻辑关系数据的线性表、树或者图结构统称为查找表。
静态查找表:查找完不会对表进行操作,
动态查找表:查找完对表进行操作
平均查找长度:所有查找次数中关键字比较次数 的平均值
数学表达式:ASL=
1.顺序查找