数据结构与算法常见问题总结-树和图

一、树和二叉树

1.树的基本概念

树是n(n>=0)个结点的有限集合,若n=0时,称为空树否则:有且仅有一个特殊的称为树的根结点;若n>1时,其余结点被分为m个互不相交的子集T1,T2,T3---Tn,每个子集本身又是一颗树,称为根的子树

2.树的基本性质

2.1 树中所有结点的个数等于所有结点的度之和+1;(分叉数+1即树中的任意一个结点(根除外都有一个分枝指向它))

2.2 对于度为m的树,定叶子结点的个数为n0,度为1的结点个数为n1,度为m的结点个数度为nm则有,n0=n2+2*n3+3*n4+(m-1)*nm+1;N=n1+n2+n3+---+nm; N=分支数+1=0*n0+1*n1+2*n2+---+nm*m+1

2.3 在非空的m度树中,第i层上至多有mi-1个结点(i>=1)An=a1*qn-1(n=I,q=m)

2.4 高度为h的M度树,最多有Mh-1/M-1个结点(Sn=a1(1-qn)/1-qèSn=a1(qn-1)/q-1)

2.5 N个结点的m度树的最小高度是logm(n*(m-1)+1)向上取整;

3.树的存储结构

3.1 双亲表示法

特点利用了父节点唯一,方便直接找到任意结点的父节点,求孩子时需要扫描整个数组

3.2 孩子链表示法

树中每个结点有多个指针域,每个指针指向其一颗子树的根结点

3.3 孩子兄弟表示法(二叉树表示法)

有两个指针域分别指向结点的第一个子节点和下一个兄弟结点。(树转换为二叉树再存储)

4.二叉树

4.1 二叉树的基本概念

4.1.1 二叉树时n(n>=0)个结点的有序集合,若n=0,则为空树,否则

4.1.2有且只有一个特殊的结点称为树的根结点;

4.1.3若n>1时,其余结点被分为两个互不相交的子集 T1,T2,分别称之为左,右子树,并且左右子树又都是二叉树。是有序树,不可交换次序。

4.2 二叉树的基本性质

4.2.1 在非空二叉树中,第i层上至多有2i-1个结点(i>=1);

4.2.2 深度为K的二叉树至多有2k-1个;

4.2.3 对于任意一颗二叉树,若叶子结点数为n0,则度为2的结点个数为n2,n0=n2+1;

4.2.4 结点个数等于所有结点的度之和+1;

4.3满二叉树

一颗深度为K且有2k-1个结点的二叉树称为满二叉树

特点:每一层节点数达到最大,满二叉树所有结点(除叶子都有左右子树)

可对满二叉树进行连续编号;

4.4 完全二叉树

如果深度为k,由n个节点的二叉树,当且仅当每个结点都与深度为k的满二叉树中编号从1—n的结点一一对应,该二叉树称之为完全二叉树。

性质:

  1. n0=n2+1;
  2. 结点个数等于所有度之和+1;   
  3. 第i层,最多有2i-1个结点
  4. 前i最多有2i-1个结点   
  5. 前4点二叉树通用性质
  6. 2k-1<=N<=2i-1
  7. 若深度为k的完全二叉树则叶子结点只会出现在最后两层
  8. 对于完全二叉树,对于任一结点,如果其右子树的最大层次为i,则左子树的最大层次为i或i+1(有右必有左(保证不跳号),有左未必有右)
  9. N个结点的完全二叉树深度为log2n向下取整+1=log2(n+1)向上取整
  10. 对于编号为i,i=1,则i结点是根结点,否则i>1,则双亲结点的编号是i/2向下取整;如果2i>n,表示i为叶子结点,无左孩子;否则左孩子为2i;如果2i+1>n,则结点i无右孩子,否则右孩子的编号为2i+1;

11.对与完全二叉树,度为1的结点个数是1或0;

4.5 二叉树的存储结构

1)顺序存储:用一组连续的存储单元,自上而下,自左而右的存储完全二叉树的数据元素,按编号存储,非完全二叉树将其每个结点与完全二叉树相对照,补位置

最坏情况下,一个深度为K且只有k个结点的单只树需要2k-1的一维数组一般情况下只有完全二叉树才使用顺序结构。

2)链式存储:二叉链表、三叉链表

4.6 二叉树的遍历

找一种规律使非线性结构的二叉树的结点排列在一个线性序列上

先序遍历:根左右 中序遍历:左根右 后续遍历:左右根 层次遍历:逐层遍历

先序+中序、后序+中序均可唯一的确认一颗二叉树

先序遍历:访问根结点,接着访问左子树,然后在访问右子树(每个左右子树也依次按照先根后左然后再右子树)

中序遍历:先访问左子树,然后访问根结点,最后访问右子树

后序遍历:先访问左子树,然后访问右子树,最后访问根结点

4.7线索二叉树

1)为什么要在二叉树中加入线索:(在采用二叉链表存储时)原来的左右孩子域有许多空指针没有利用起来,(n个节点,则有n-1条边(指针连线)),而n个结点共有2n个指针域(Lchild和Rchild),显然有n+1个空指针域未用),为了不浪费存储空间,利用这些空闲的指针域来存放结点的直接前驱和直接后继(按某种遍历线性化后序列中的前驱和后继)

2)如何线性化:若结点有左孩子则,Lchild指向结点的左孩子,否则指向前驱;若结点有右孩子,则Rchild指向结点的右孩子,否则指向后继。结点结构如下

Ltag=0:Lchild->结点左孩子;Ltag=1:Lchild->结点前驱

Rtag=0:Rchild->结点右孩子;Rtag=1:Rchild->结点后继

 4.8树和二叉树的转换

1)加虚线,在树的每层按从左至右的顺序在兄弟之间加虚线

2)去连线,除最左边的第一个子节点外,父节点和所有其他子节点的连线去掉

3)旋转,顺时针旋转45°,原来实线左斜

4)整型,旋转后所有虚线改为实线

树转换为二叉树后特点:左孩右兄

  1. 二叉树的根结点没有右子树,只有左子树
  2. 左子结点仍然时原来树相应结点的左子结点,而延右链往下的右子节点均是原来树中该节点的兄弟结点

4.9森林转换为二叉树

1)先将每棵森林转换为二叉树

2)把每一棵二叉树当作前一棵二叉树的右子树

反之,去二叉树的右链全部去掉,依次还原

4.10 树的遍历

先序=二叉先序,后序=二叉树中序

4.11哈夫曼树

最优二叉树:结点路径,从树中一个结点到另一个结点之间的分支构成这两个结点间的路径

路径长度:经过分支数目

结点的带权路径长度:路径数量*权重

WPL:带权路径长度之和

Huffman树构造:

① 根据n个权值{w1,w2,w3,---wn}构造成n棵二叉树的集合F{T1,T2---Tn},其中没课二叉树只   有一个权值Wi的根结点没有左右子树

②重复②、③直到F中只包含一颗树

③在F中删除这两颗树,同时将新得到的树加入F中

④在F中选取两棵根结点权值最小的树作为左、右子树构造一颗新的二叉树,且新的二叉树根结点的权值为其左右子树根结点的权值之和

二、图

1.图的基本概念

1)无向图:顶点数为n,边用e表示,则:e∈[0,n*(n-1)/2],若边等于n*(n-1)/2称为完全无向图,任意顶点都有边直接相连

2)有向图:n个顶点,边的数目为e∈[0,n*(n-1)],具有n*(n-1)条边的有向图为,完全有向图

3)子图:边也是,顶点也是原图的子集

4)生成子图:顶点需全部包含,边也属于原图的子集

5)无向图的度之和是e的2倍

6)有向图的度之和等于出度+入度之和

7)路径:是图G中连接两顶点之间所经过的的顶点序列

8)路径长度:路径上边或是弧的数目

9)简单路径:在一条路径中若没有重复的顶点

10)简单回路:除了第一个顶点和最后一个顶点外,其余顶点不重复出现在回路中

11)连通图:图中任意两个顶点都是连通(任意两个顶点之间都有边连通可中转)

12)连通分量:若G是非连通图,则极大连通子图称为G的连通分量(极大的含义就是对子图再增加G中的其他顶点,子图就不再连通)

12)强连通图:对有向图而言

13)强连通分量:对于有向图

14)生成树:一个连通图的(无向图)的生成树是一个极小连通子图,它包含图中全部顶点和只有足以构成一颗树的n-1条边,称为图的生成树

15)有向树:只有一个顶点的入度为0,其余顶点均入度为1的有向图

16)带权图:网

2.无向图的邻接矩阵

1)用一维数组Vexs[n]存储顶点信息,用二维矩阵A[n][n]来存储顶点之间关系的信息,该二维数组称为邻接矩阵;以顶点再数组Vexs中的下标表示顶点,邻接矩阵中的元素A[i][j]中存储的是顶点i到顶点j之间关系的信息

                                              无向无权图的数组存储

2)无向带权图邻接矩阵:有边直接相连,写权重,无边直接相连写无穷大

3)无向图邻接矩阵的特性

  • 邻接矩阵是方方阵
  • 邻接矩阵是唯一的
  • 对于顶点Vi,其度是第i行的非零元素的个数
  • 无向图的边数是上或下三角矩阵中非零元素的个数
  • 空间复杂度:O(V2)
  • 设图G的邻接矩阵为A,An的元素An[i][j]等于由顶点i到顶点j的长度为n的路径数目

3.有向图的邻接矩阵

1)类比于无向图的邻接矩阵:行表示出度,列表示入度

2)有向图的邻接矩阵的特性:

  • 对于顶点Vi,第i行非0元素的个数是其出度OD(vi),第i列非0元素个数则表示入度
  • 邻接矩阵中非零元素的个数就是图的弧的数目
  • 空间复杂度:O(V2)

4.邻接矩阵的特性

1)给定图,邻接矩阵唯一

2)邻接矩阵所占的空间开销是O(N2)N为顶点个数,与边数无关

3)适合于稠密图

5.邻接矩阵的定义

#define  INFINITY MAX_val//最大值

#define MAX_VERTEX_NUM 100//顶点数目的最大值

typedef char VertexType;//顶点的数据类型

typedef int EdgeType;//带权图中边上权值的数据类型

//定义边信息

typedef struct Arctype

{

     int vex1, vex2;//弧或是边依附的两个顶点

     int Arcval;//边或是弧权值的信息

}Arctype;//边或是弧的结构定义

typedef struct

{

     int vexnum, arcnum;//顶点数目,边数目

     VertexType vex[MAX_VERTEX_NUM];//顶点表

     EdgeType Edge[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//边表

}MGraph;//图的结构

6.邻接表

1)对图的每个顶点建立单链表,存储该顶点所有邻接顶点其相关信息

2)

    1. 邻接表的特点
  • 表头向量每个分量就是单链表的头结点,分量个数就是图中顶点数目
  • 在边或弧稀疏的条件下,邻接表比邻接矩阵更节省空间(无向图:O(N+2e),有向图:O(N+e))
  • 在无向图,顶点Vi的度是第i个链表的节点数(正向出)

7.邻接表的定义

1)定义边结构

2)定义图结构

3)定义表结点

4)定义表头结点

8.十字链表和邻接多重表

1)十字链表是有向图的一种链式存储结构,空间复杂度:O(v+e)

2)多重邻接表是无限图的另一种链式存储结构,空间复杂度:O(v+e)

9.图的深度优先遍历DFS

1)图的遍历:从图的某一顶点出发,访遍图中其余顶点,且每个顶点仅访问一次

在遍历过程中,记录下已经访问过的顶点,设置一个辅助向量Visited[1---n],n为顶点个数,其初始值为0,一旦访问了Vi顶点后,使Visited[i]=1或为访问序号。

2)思想1:

  • 设初始时图中所有顶点均未被访问;
  • 从图中某个顶点Vi出发,访问vi,然后找到与Vi邻接的顶点Vi1;
  • 从Vi1出发,深度优先搜索访问和Vi1相邻接且未被访问的所有顶点;
  • 转至1,直到和Vi相邻接的所有顶点都被访问为止
  • 继续选取图中未被访问的结点Vj作为起点,转至1,直到图中所有结点都访问完为止

3)思想2:

  • 首先访问结点i,将其标记为访问过即,visited[i]=1;
  • 然后搜索与i有边相连的下一个顶点j,若j未被访问,则访问它并将visited[j]=1;然后从j开始重复此过程,若j已经访问则看与i有边相邻的其他顶点;
  • 若与i有边相连的顶点都被访问过了,则回退到前一个访问的结点重复刚才的过程直到图中所有顶点都被访问完为止

4)  同理DFS:需要借助一个工作栈最坏情况下,邻接矩阵和邻接表的存储方式空间复杂度O(v) 

时间复杂度:邻接表:O(v+e);邻接矩阵:O(v2)

10.图的广度优先遍历BFS

1)思想1:谁访问谁进队,出队谁与之相关的全部进队

开始时,将visited数组置为空;

在每访问一个顶点时,将其入队

在访问一个顶点的所有后继时将其出队

若队列为空,则说明每个访问过的顶点的所有后继均已访问完毕,因而本次遍历可以结束,若此时还有未访问的顶点,则另选起点进行访问。

2)BFS无论是邻接矩阵和邻接表存储方式,BFS都需要借助一个队列,最坏情况下空间复杂度O(v)

对于时间复杂度:邻接表:O(v+e);邻接矩阵:O(v2)

11.图的遍历和图的连通性

1)对于无向图,调用BFS(G,i)或是DFS(G,i)的次数等于该图的连通分量个数

2)对于有向图,连通的有向图分为强连通和非强连通分量,分强连通一次调用BFS或DFS无法访问到该连通分量的所有顶点。

12. 图的应用

1)最小生成树的两种算法:Prim和Kruskal

Prim:初始时从图中选取任一顶点加入树T,此时树中只包含一个顶点,之后选择一个于当前T中顶点集合距离最近的顶点,并将该顶点和相应的边加入T,每次操作后T中的顶点数+1,以此类推,直至图中所有顶点都加入T,此时T中必然有n-1条边。Prim的时间复杂度为O(v2),不依赖于e,因此它适合求稠密图

Kruskal:①初始时为只有n个顶点而无边的非连通图T={V,{}},每个顶点自成一个连通分量,然后按边的权值由小到大进行排序,不断选取当前未被选取过且权值最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入T,否则舍弃此边选择下一条最小的边。以此类推直至T中所有的顶点都在一个连通分量上。

②:将图中所有的边按权值递增顺序排序,依次选定权值较小的边,但要求后面选取的边不能和前面选取的边构成回路,若构成回路,就放弃该条边,再去选取后面权值次小的边,n个顶点选取n-1条边即可。时间复杂度为:O(ElogE),适合边稀疏而顶点较多的图

2)最短路径:单源点:Dijkstra和每对顶点:Floyd

当图时带权图时,把从一个顶点v0到其余任意一个顶点的vi的一条路径所经过的权值之和定义为该路径的带权路径长度,带权路径长度最短的那条路径即最短路径。

Dijkstra:时间复杂度:邻接表或是邻接矩阵O(v2);不适合边上带负权值

Floyd:时间复杂度:邻接表或是邻接矩阵O(v3);允许有带负权值的边,但不允许包含带负权值的边构成回路

3)有向无环图描述表达式:可以对相同表达式进行共享,从而节省存储空间

4) 拓扑排序

AOV网:做事的先后顺序,其顶点表示活动,用有向边<Vi,Vj>表示活动Vi必须先于活动Vj进行的这样一种关系,则将这种有向图称为顶点表示活动的网络

常用方法:

  • 从AOV网中选取一个没有前驱的顶点并输出
  • 从网中删除该顶点和所有以它为起点的有向边‘
  • 重复上述①和②步骤直到当前AOV网为空或当前网中不存在无前驱的顶点为止。后一种情况说明有向图中必然有环
  • 此外DFS也能实现拓扑排序

时间复杂度:邻接表:O(v+e);邻接矩阵:O(v2)

对于一般图来说,邻接矩阵时三角矩阵,则存在拓扑排序,但反之不一定

5)关键路径

AOE网:在带权图中,以顶点表示事件,有向边表示活动,以边上的权值表示完成活动的开销

AOE和AOV网都是有向无环图,AOE的边有权值,而AOV的边无权值。

性质:只有在某顶点所代表的时间发生后,从该顶点出发的各有向边所代表的活动才能开始

           只有在进入某顶点的各有向边代表的活动都已经结束时,该顶点所代表的事件才能发生

求解过程:

事件Vk的最早发生时间Ve(k):Ve(i)=max{V(j)+w(j,k)},w(j,k)表示<vj,vk>上的权值:正向

Ve(1)=0,Ve(2)=3,Ve(3)=2,Ve(4)=max{Ve(2)+2,Ve(3)+4}=max{5,6}=6,Ve(5)=6,Ve(6)=max{Ve(5)+1,Ve(4)+2,Ve(3)+3}=max{7,8,5}=8

事件Vk的最迟发生时间Vl(k):Vl(i)=min{Vl(k)-w(k,j)},vk为vj的任意前驱:逆向

Vl(6)=8,Vl(5)=Vl(6)-1=7,Vl(4)=Vl(6)-2=6,Vl(3)=min{Vl(4)-4,Vl(6)-3}=min{2,5}=2,Vl(2)=min{Vl(4)-2,Vl(5)-3}=min{4,4}=4,Vl(1)=min{Vl(3)-2,Vl(2)-3}=min{0,1}=0

V1

V2

V3

V4

V5

V6

Ve(i)

0

3

2

6

6

8

Vl(i)

0

4

2

6

7

8

活动ai的最早开始时间e(i):指该活动弧的起点所表示事件的最早发生时间

活动ai的最迟开始时间l(i):它是指该活动弧终点所表示的事件的最迟发生时间和该活动的时间之差:l(i)=vl(j)-w{k,j},<vk,vj>表示活动ai:

如:L(1)=vl(2)-3

D(i)=L(i)-e(i)表示该活动完成的时间余量

A1

A2

A3

A4

A5

A6

A7

A8

e(i)

0

0

3

3

2

2

6

6

l(i)

1

0

4

4

2

5

6

7

l(i)-e(i)

1

0

1

1

0

1

0

1

A2,A5,A7为关键活动对应的v1,v3,v4,v6为关路径

若选择或只要结果;从源点开始遍历出到终点的所有可能,其中权值之和最大的就是关键路径

如上述:Max{v1-v2-v5-v6=6/v1-v2-v4-v6=7/v1-v3-v4-v6=8/v1-v3-v6=5}=v1-v3-v4-v6

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值