赞
踩
定义:将算法中基本运算的执行次数的数量级作为时间复杂度,记为 O ( n ) O(n) O(n)。
计算原则
计算方法
定义:指算法运行过程中所使用的辅助空间的大小,记为 S ( n ) S(n) S(n)。算法原地工作是指算法所需辅助空间是常量,即 O ( 1 ) O(1) O(1)。
递归算法特性:
- 一个算法直接或间接调用自身,称为递归调用。
- 必须有一个明确的递归结束条件,称为递归出口。
- 实现递归的关键是建立递归调用工作栈。栈的大小也就是递归深度和递归算法空间复杂度的。
定义:指用一组连续的存储单元,依次存储线性表中的各个元素,从而使得逻辑上相邻的元素在物理位置上也相邻,因此可以随机存取(根据首元素地址和元素序号)表中任何一个元素。
基本操作
结构
插入
若表长为 n n n,下标从0开始,则在第 i i i位置插入元素 e e e,则从 a n a_n an到 a i a_i ai都要向后移动一个位置,共需移动 n − i + 1 n-i+1 n−i+1个元素,平均时间复杂度为 O ( n ) O(n) O(n)。
//判断1的范围是否有效,否则非法
//判断当前存储空间是否已满,否则不能插入
for(int j=L.length;j>=i;--) //将第1个位置及之后的元素后移
L.data[j]=L.data[j-1];
L.data[i-1]=e; //在位置i处放入e,数组从0开始存储
L.length++; //线性表长度加1
删除
若表长为 n n n,下标从 0 0 0开始,当删除第 i 个元素时,则从 i个元素时,则从 i个元素时,则从 a i + 1 a_{i+1} ai+1 到 a n a_n an都要向前移动一个位置,共需移动 n − i n-i n−i个元素,平均时间复杂度为 O ( n ) O(n) O(n)。
//判断1的范围是否有效
for(int j=i;j<L.length;j++) //将第i个位置及之后的元素前移
L.data[j-1]=L.data[j];
L.length--; //线性表长度减1
查找
定义:为了建立元素之间的线性关系,对每个链表结点,除了存放元素自身的信息,还需要存放一个指向其后继的指针。
基本操作
查找
只能从表中第一个结点出发顺序查找,顺着指针 n e x t next next域逐个往下搜索,直到找到满足条件的结点为止。时间复杂度为 O ( n ) O(n) O(n)。
创建
头插法:从一个空表开始,然后将新结点插入到当前链表的表头,即头结点之后。
s->next=L->next; //①新结点的指针指向原链表的第一个结点
L->next=s; //②头结点的指针指向新结点,L为头指针
尾插法:将新结点插入到当前链表的表尾上,为此必须增加一个尾指针 r r r,使其始终指向当前链表的尾结点。
r->next=s; //原链表中的尾结点(r所指)的指针指向新结点
r=s; //r指向新的表尾结点
插入
将值为 x x x的新结点插入到单链表的第 i i i个位置。先检查插入位置的合法性,然后找到待插入位置的前驱结点,即第 i − 1 i-1 i−1 个结点,再在其后插入新结点。
p=GetElem(L,i-1); //查找插入位置的前驱结点
s->next=p->next; //①s的指针指向p的下一结点
p->next-s; //②p的指针指向s
删除
将单链表的第 i i i个结点删除。先检查删除位置的合法性,然后查找表中第 i − 1 i-1 i−1 个结点,即被删结点的前驱结点,再将其删除。
p=GetElem(L,i-1); //查找删除位置的前驱结点
q=p->next; //令q指向被删除结点
p->next=q->next; //将*q结点从链中"断开"
delete q; //释放待删结点所占空间
求表长
len=0; //1en表示单链表长度,初值设为0
LNode *p=L->next; //令p指向单链表的第一个结点
while(p) {len++;p=p->next;} //跳出循环时,len的值即为单链表的长度
术语 | 定义 |
---|---|
栈 | 只允许在表的一端(栈顶)进行插入或删除的线性表。栈的操作特性为先进后出。 |
队列 | 只允许在表尾(队尾)插入,表头(队头)删除的线性表。队列的操作特性为先进先出。 |
n n n个不同的元素进栈,则不同出栈序列个数为 1 n + 1 C 2 n n \frac{1}{n+1} C_{2 n}^{n} n+11C2nn。
定义:把顺序存储的队列从逻辑上视为一个环,称为循环队列。通常利用除模取余运算 ( % ) (\%) (%)来实现循环。
基本操作
元素出队:Q.front=(Q.front+1)%MaxSize
元素入队:Q.rear=(Q.rear+l)%MaxSize
队列长度:(Q.rear-Q.front+-MaxSize)%MaxSize
(
f
r
o
n
t
front
front指向第一个元素、
r
e
a
r
rear
rear指向最后一个元素的下一位置)
为了区分是队空还是队满,通常采用牺牲一个存储单元的方法,约定以“队头指针在队尾指针的下一位置作为队满的标志”。
队满条件:
(Q.rear+1)%MaxSize==Q.front
队空条件:
Q.front==Q.rear
定义:允许两端都可以进行入队和出队操作的队列,即每个元素仅有一个前驱结点和一个后继结点(首位元素除外)。将队列的两端分别称为前端和后端。
基本操作
进队:前端进的元素排在后端进的元素的前面,后端进的元素排在前端进的元素的后面。
出队:无论是前端出队还是后端出队,先出的元素排列在后出的元素的前面。
定义:允许在一端进行插入和删除,但在另一端只允许插入的双端队列。
定义:允许在一端进行插入和删除,但在另一端只允许删除的双端队列。
栈的应用 | 队列的应用 |
---|---|
数制转换 | 层序遍历二叉树 |
括号匹配 | 缓冲区 |
表达式求值 | 进程的就绪队列 |
给定进栈和出栈顺序确定该栈的最小容量(最大深度) | 广度优先遍历 B F S BFS BFS |
存储方式
特殊矩阵
对称矩阵
存储方式:只存放主对角线和下三角区的元素。
三角矩阵
下三角矩阵:上三角区的所有元素均为同一常量,存储完下三角区和主对角线上的元素之后,紧接着存储对角线上方的常量一次。
上三角矩阵:下三角区的所有元素均为同一常量,存储完上三角区和主对角线上的元素之后,紧接着存储对角线下方的常量一次。
简单模式匹配算法
算法思想:从主串 S S S的第一个字符起,与模式 T T T的第一个字符比较,若相等,则继续逐个比较后续字符;否则从主串的下一个字符起,重新和模式的字符比较;以此类推,直至模式 T T T中的每个字符依次和主串 S S S 中一个连续的字符序列相等,则称匹配成功,否则称匹配不成功。简单模式匹配算法的最坏时间复杂度为 O ( m n ) O(mn) O(mn)。
算法代码
int Index(SString S,SString T){
int i=1,j=1;
while(i<=S.length && j<=T.length){
if(S.ch[i]==T.ch[j]){
++1;++j;//继续比较后继字符
}else{
i=i-j+2;j=1;//指针后退重新开始匹配
}
}
if(j>T.length) return i-T.length;
else return 0;
}
K M P KMP KMP 算法
前缀:指除最后一个字符以外,字符串的所有头部子串。
后缀:指除第一个字符外,字符串的所有尾部字串。
部分匹配值:字符串的前缀和后缀的最长相等前后缀长度。
匹配失败时子串需要向后移动的位数:移动位数=已匹配的字符数-最后一位匹配字符对应的部分匹配值。
Next 数组计算
树的基本术语
术语 | 定义 |
---|---|
结点的度 | 树中一个结点的子结点个数 |
树的度 | 树中结点的最大度数 |
分支结点 | 度大于 0 的结点称为分支结点 |
叶子结点 | 度等于 0 的结点称为叶子结点 |
结点的深度 | 从根结点到该结点的路径上结点的个数之和(根结点深度为 1) |
树的高度 | 树中结点的最大层数(根结点为第一层) |
路径 | 树中两个结点之间所经过的结点序列 |
路径长度 | 路径上所经过的边的条数 |
- 树可以是空树(结点数=0)。
- 森林可以是空森林(树个数=0)。
树的基本性质
术语 | 定义 |
---|---|
满二叉树 | 一棵高度为 h h h,并且含有 2 h − 1 2^h-1 2h−1个结点的二叉树称为满二叉树。满结点。 |
完全二叉树 | 一棵高度为 h h h,有 n n n个结点的二叉树,仅当其每个结点都与高度为 h h h个结点的二叉树,仅当其每个结点都与高度为 h h h的满二叉树中编号为 1 ∼ n 1\sim n 1∼n 的结点一一对应,称为完全二叉树。 |
遍历方式
构造二叉树
唯一确定一棵二叉树的序列组合(都需要中序序列)
规则:左结点指向孩子结点,右结点指向兄弟结点(左孩子右兄弟)。
结点结构
线索:利用空链域存放指向直接前驱或直接后继的指针。
线索化:对二叉树遍历使其变为线索二叉树。线索化时,若无左子树,则令
I
c
h
i
l
d
Ichild
Ichild指向其前驱结点;若无右子树,则令
r
c
h
i
l
d
rchild
rchild指向其后继结点。两个
t
a
g
tag
tag标志域表明当前指针域所指对象是指向左(右)子结点还是直接前驱(后继)。
ltag
=
{
0
,
lchild域指示结点的左孩子
1
,
lchild域指示结点的前驱
rtag
=
{
0
,
rchild域指示结点的右孩子
1
,
rchild域指示结点的后继
\text { ltag }=\left\{
在有 N 个结点的二叉树中,有 N+1 个空指针域。
树的带权路径长度:树中所有叶结点的带权路径长度之和。
w
i
w_i
wi是第
i
i
i个叶结点所带的权值;
l
i
l_i
li是该叶结点到根结点的路径长度。
W
P
L
=
∑
i
=
1
n
w
i
l
i
\mathrm{WPL}=\sum_{i=1}^{n} w_{i} l_{i}
WPL=i=1∑nwili
哈夫曼树:带权路径长度最小的二叉树。
构造哈夫曼树
哈夫曼树特点
前缀编码:没有一个编码是另一个编码的前缀。
哈夫曼编码构造
根据待编码元素构造对应的哈夫曼树。
从根至该字符的路径上进行标记,“左孩子”的边标记为 0 0 0,“右孩子”的边标记为 1 1 1。
从根结点到待编码元素结点的路径形成的 0 0 0、 1 1 1序列就是哈夫曼编码。
无向图 | 有向图 |
---|---|
边:顶点的无序对,记为 ( v , w ) (v,w) (v,w)或 ( w , v ) (w,v) (w,v) | 弧:顶点的有序对,记为 < v , w > <v,w> <v,w>, v v v称为弧尾, w w w 称为弧头 |
无向图:若 E E E是无向边(简称边)的有限集合时,则图 G G G为无向图 | 有向图:若 E E E是有向边(也称弧)的有限集合时,则图 G G G为有向图 |
顶点的度:指依附于该顶点的边的数量,记为 T D ( v ) TD(v) TD(v) | 顶点的度:入度和出度之和(入度是以 v v v为终点的有向边的数目;出度是以 v v v为起点的有向边的数目) |
连通:从顶点 v v v到顶点 w w w有路径存在,则称 v v v和 w w w 是连通的 | 强连通:从顶点 v v v到顶点 w w w和从顶点 w w w到顶点 v v v之间都有路径,则称 v v v和 w w w 是强连通的 |
连通图:图中任意两个顶点都是连通的,则称图 G G G为连通图,否则为非连通图 | 强连通图:图中任意一对项点都是强连通的,则称此图为强连通图 |
连通分量:图的极大连通子图称为连通分量 | 强连通分量:图的极大强连通子图称为有向图的强连通分量 |
无向完全图:任意两个顶点之间都存在边,共有 n ( n − 1 ) / 2 n(n-1)/2 n(n−1)/2条边 | 有向完全图:任意两个顶点之间都存在方向相反的两条弧,共有 n ( n − 1 ) n(n-1) n(n−1)条有向边 |
术语 | 定义 |
---|---|
生成树 | 连通图的生成树是包含图中全部顶点的一个极小连通子图 |
生成森林 | 非连通图的连通分量的生成树构成了非连通图的生成森林 |
回路(环) | 第一个顶点和最后一个顶点相同的路径称为回路或环 |
- 极大连通子图:包含了图中尽可能多的顶点以及尽可能多的边
- 极小连通子图:包含图中全部顶点且包含边最少
方式:用一个一维数组存储图中顶点的信息,用一个二维数组存储图中边的信息
特点
无向图的邻接矩阵一定是一个对称矩阵,可采用压缩存储。有向图的邻接矩阵不一定是对称矩阵,有向完全图的邻接矩阵是对称矩阵。
对于无向图,邻接矩阵的第 i i i行(或第 i i i列)非零元素(或非 ∞ ∞ ∞元素)的个数正好是第 i i i个顶点的度。
对于有向图,邻接矩阵的第 i i i行(或第 i i i列)非零元素(或非 ∞ ∞ ∞元素)的个数正好是第 i i i个顶点的出度(或入度)。
对于无权图的邻接矩阵 A A A(矩阵中各位置的值为 0 0 0或 1 1 1), A n [ i ] [ j ] \left.A^{n}[i][j\right] An[i][j]的值表示由顶点 i i i到顶点 j j j的路径长度为 n n n的路径的数量。
稠密图适合使用邻接矩阵的存储表示。
方式
特点:
若无向图有 n n n个顶点、 e e e条边,则其邻接表仅需 n n n个头结点和 2 e 2e 2e个表结点。
对于无向图,顶点 v i v_i vi的度等于第 i i i个链表中的结点数;对于有向图,第 i i i 个链表中的结点数只是顶点 v i v_i vi的出度,求入度,必须遍历整个邻接表。
在邻接表上容易找到任一顶点连接的所有边。但要判定任意两个顶点( v i v_i vi和 v j v_j vj)之间是否有边或弧相连,则需搜索第 i i i个或第 j j j 个链表,在这方面不及邻接矩阵方便。
图的邻接表不唯一,取决于建立邻接表的算法以及边的输入次序。
遍历过程
性能分析
遍历过程
性能分析
对于同一个图,基于邻接矩阵的遍历所得到的 D F S DFS DFS序列和 B F S BFS BFS序列是唯一的;由于邻接表不唯一,所以基于邻接表的遍历所得到的 D F S DFS DFS序列和 B F S BFS BFS序列可能不唯一。
定义:所有生成树的集合中边的权值之和最小的那棵生成树。
性质
每次在已选顶点的邻接边中选权值最小的边加入。
P r i m Prim Prim算法的时间复杂度为 O ( ∣ V ∣ 2 ) O(|V|^2) O(∣V∣2),不依赖于 E E E,适用于边稠密的图。
不断选取当前未被选取的权值最小的边,若其两个顶点不属于同一顶点集合,则将该边放入生成树的边集,同时将两个顶点所在的集合合并。
直到选取了 n − 1 n-1 n−1条边为止。
K r u s k a l Kruskal Kruskal算法的时间复杂度为 O ( ∣ E ∣ log 2 ∣ E ∣ ) O\left(|E| \log _{2}|E|\right) O(∣E∣log2∣E∣),适用于边稀疏而顶点较多的图。
用邻接矩阵
a
r
c
s
arcs
arcs表示带权有向图
arcs
[
i
]
[
j
]
=
{
e
i
j
,
e
i
j
为有向边
<
i
,
j
>
的权值
∞
,
不存在有向边
<
i
,
j
>
\operatorname{arcs}[i][j]=\left\{
算法过程
d i s t [ ] dist[] dist[]:记录了从源点 v 0 v_0 v0到其他各顶点当前的最短路径长度。
集合 S S S 记录已求得的最短路径的顶点,每趟确定一个最短路径。
算法过程
定义一个 n 阶方阵序列: A ( − 1 ) , A ( 0 ) , ⋯ , A ( n − 1 ) 。 A^{(-1)}, A^{(0)}, \cdots, A^{(n-1)}。 A(−1),A(0),⋯,A(n−1)。
A ( − 1 ) [ i ] [ j ] = arcs [ i ] [ j ] A^{(-1)}[i][j]=\operatorname{arcs}[i][j] A(−1)[i][j]=arcs[i][j] ( ( ( arcs [ i ] [ j ] \operatorname{arcs}[i][j] arcs[i][j]与迪杰斯特拉算法中的定义相同 ) ) )。
递推产生 A ( k ) [ i ] [ j ] = Min { A ( k − 1 ) [ i ] [ j ] , A ( k − 1 ) [ i ] [ k ] + A ( k − 1 ) [ k ] [ j ] } , k = 0 , 1 , ⋯ , n − 1 A^{(k)}[i][j]=\operatorname{Min}\left\{A^{(k-1)}[i][j], \quad A^{(k-1)}[i][k]+A^{(k-1)}[k][j]\right\}, \quad k=0,1, \cdots, n-1 A(k)[i][j]=Min{A(k−1)[i][j],A(k−1)[i][k]+A(k−1)[k][j]},k=0,1,⋯,n−1。
A ( k ) [ i ] [ j ] A(k)[i][j] A(k)[i][j]是从顶点 v i v_i vi 到 到 到 v j v_j vj、中间顶点的序号不大于 k k k的最短路径的长度。
经过 n n n次迭代后所得到的 A ( n − 1 ) [ i ] [ j ] A(n-1)[i][j] A(n−1)[i][j]就是 v i v_i vi到 v j v_j vj 的最短路径长度。
Floyd 算法的时间复杂度为 O ( ∣ V ∣ 3 ) O(|V|^3) O(∣V∣3)。
Floyd 算法允许图中有带负权值的边,但不允许有包含带负权值的边组成的回路。
定义
算法过程
拓扑排序的时间复杂度为 O ( ∣ V ∣ + ∣ E ∣ ) O(|V|+|E|) O(∣V∣+∣E∣)。
AOE 网:带权有向图以顶点表示事件,有向边表示活动,边上的权值表示完成该活动的开销。
关键路径:AOE 网中从源点到汇点的最大路径长度的路径,关键路径长度是整个工程所需的最短工期。
关键活动:关键路径上的活动。
性质
参数定义
ve
(
k
)
=
{
0
,
k
=
0
,
v
k
是源点
max
{
ve
(
j
)
+
Weight
(
v
j
,
v
k
)
}
,
Weight
(
v
j
,
v
k
)
表示
<
v
j
,
v
k
>
上的权值
\operatorname{ve}(k)=\left\{
vl
(
j
)
=
{
v
e
(
n
−
1
)
,
j
=
n
−
1
,
v
j
是汇点
min
{
v
l
(
k
)
−
Weight
(
v
j
,
v
k
)
}
,
Weight
(
v
j
,
v
k
)
表示
<
v
j
,
v
k
>
的权值
\operatorname{vl}(j)=\left\{
e ( i ) = v e ( k ) e(i)=ve(k) e(i)=ve(k)
l ( i ) = vl ( j ) − Weight ( v k , v j ) \mathrm{l}(i)=\operatorname{vl}(j)-\operatorname{Weight}\left(\mathrm{v}_{k}, \mathrm{v}_{j}\right) l(i)=vl(j)−Weight(vk,vj)
算法过程
求 A O E AOE AOE网中所有事件的最早发生时间 v e ( ) ve() ve()
从源点出发,到该顶点的最大长度。
求 AOE 网中所有事件的最迟发生时间 v l ( ) vl() vl()
事件的最迟发生时间=关键路径长度-从该顶点到汇点的最大长度。
求 AOE 网中所有活动的最早开始时间 e ( ) e() e()
活动的最早开始时间=该活动的起点所代表的事件的最早发生时间。也就是从源点出发,到这条边的起始点的最大长度。
求 AOE 网中所有活动的最迟开始时间 l ( ) l() l()
活动的最迟开始时间=关键路径长度-(从这条边的终点到汇点的最大长度 + 这条边的长度)。
求 AOE 网中所有活动的差额 d ( ) = l ( ) − e ( ) d()=l()-e() d()=l()−e(),找出所有 d ( ) = 0 d()=0 d()=0(最早开始时间=最迟开始时间)的活动构成关键路径。若活动的最迟开始时间 > 最早开始时间,则该活动不是关键活动。
查找:在数据集合中寻找满足某种条件的数据元素的过程称为查找。查找的结果一般分为查找成功、查找失败。
查找表(查找结构):用于查找的数据集合称为查找表,它由同一类型的数据元素(或记录)组成,可以是一个数组或链表等数据类型。对查找表经常进行的操作一般有 4 4 4 种:
静态查找表:若一个查找表的操作只涉及上述操作 ① 和 ②,则无须动态地修改查找表,此类查找表称为静态查找表;需要动态地插入或删除的查找表称为动态查找表。适合静态查找表的查找方法有顺序查找、折半查找、散列查找等;适合动态查找表的查找方法有二叉排序树的查找、散列查找等。二叉平衡树和 B 树都是二叉排序树的改进。
关键字:数据元素中唯一标识该元素的某个数据项的值,使用基于关键字的查找,查找结果应该是唯一的。
平均查找长度:在查找过程中,一次查找的长度是指需要比较的关键字次数,而平均查找长度则是所有查找过程中进行关键字的比较次数的平均值,其数学定义为
A S L = ∑ i = 1 n P i C i \mathrm{ASL}=\sum_{i=1}^{n} P_{i} C_{i} ASL=i=1∑nPiCi
式中, n n n是查找表的长度; P i P_i Pi是查找第 i i i个数据元素的概率,一般认为每个数据元素的查找概率相等, P i = 1 / n P_i=1/n Pi=1/n; C i C_i Ci是找到第 i i i 个数据元素所需进行的比较次数。平均查找长度是衡量查找算法效率的最主要的指标。
算法:逐个比较查找表中的元素,直到找到目标元素或达到查找表的尽头为止,时间复杂度为 O ( n ) O(n) O(n)。
一般线性表
查找成功时,在等概率的情况下,平均查找长度为 A S L 成功 = ∑ i = 1 n P i ( n − i + 1 ) = n + 1 2 \mathrm{ASL}_{\text {成功 }}=\sum_{i=1}^{n} P_{i}(n-i+1)=\frac{n+1}{2} ASL成功 =∑i=1nPi(n−i+1)=2n+1。
查找不成功时,平均查找长度为 A S L 不成功 = n + 1 \mathrm{ASL}_{\text {不成功 }}=n+1 ASL不成功 =n+1。
算法代码
typedef struct( //查找表的数据结构
ElemType *elem; //元素存储空间基址,建表时按实际长度分配,0号单元留空
int TableLen; //表的长度
}SSTable;
int search seq(SSTab1e ST,ElemType key){
ST.elem[0]=key;
for(i=ST.Tab1eLen;ST.elem[i]!=key;--i);
return i; //若表中不存在关键字为key的元素,将查找到i==0时退出for循环
}
在算法中,将下标为 0 0 0的元素当作“哨兵”。引入它的目的是使得 S e a r c h _ S e q Search\_Seq Search_Seq内的循环,不必判断数组是否会越界,因为满足 i = = 0 i==0 i==0 时,循环一定会跳出。
有序线性表
算法:表内关键字有序的,则查找失败时可以不用再比较到表的另一端就返回查找失败的信息。
查找判定树
圆形结点表示有序线性表中存在的元素。
矩形结点称为失败结点(若有 n 个结点,则相应地有 n+1 个查找失败结点),描述的是那些不在表中的数据值的集合。
效率分析
查找成功时,在等概率的情况下,平均查找长度为 A S L 成功 = ∑ i = 1 n P i ( n − i + 1 ) = n + 1 2 \mathrm{ASL}_{\text {成功 }}=\sum_{i=1}^{n} P_{i}(n-i+1)=\frac{n+1}{2} ASL成功 =∑i=1nPi(n−i+1)=2n+1。
查找不成功时,平均查找长度为 A S L 不成功 = 1 + 2 + ⋯ + n + n n + 1 = n 2 + n n + 1 \mathrm{ASL}_{\text {不成功 }}=\frac{1+2+\cdots+n+n}{n+1}=\frac{n}{2}+\frac{n}{n+1} ASL不成功 =n+11+2+⋯+n+n=2n+n+1n。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。