- 图的定义和术语
G = (V,E)
Graph = (vertex , Edge)
V : 顶点(数据元素)的有穷非空集合;
E : 边的有穷集合
- 完全图:
任意两个点都有一条边相连
无向完全图:
有n个顶点
n(n-1)/条边
有向完全图:
有n个顶点
n(n-1)条边
- 稀疏图
有很少边或者弧的图(e
- 稠密图
有很多边或者弧的图
- 网:
边/弧带权的图
- 邻接
有边/弧相连的两个顶点之间的关系
()表示不分先后,vi 和vj 互为邻接点
vi到vj
- 顶点的度
与该点相关联的边的数目,记为TD(V)
有向图
ID: 入度
OD: 出度
度=ID + OD
- 路径
连续的边构成的顶点序列。
- 路径长度
路径上边或弧的数目/权值之和。
- 回路
第一个顶点和最后一个顶点相同的路径。
- 简单路径
除了起点和终点可以相同外,其余顶点均不相同的路径,途径两个相同的点,就是非简单路径
12.简单回路
除路径起点和重点相同外,其余顶点均不相同的路径。
起点和终点相同,途径两个相同的点,就是非简单回路
- 连通图
在无(有)向图G={V,{E}}中,若任何两个顶点v、u 都存在从v到u的路径,则称G是连通图(强连通图)
- 权和网
图中的边或者弧具有相关数称为权,表明从一个顶点到另一个顶点的距离或者耗费
15.连通分量
无向图G的极大连通子图称为G的连通分量。
- 强连通分量
有向图G的极大强连通子图称G的强连通分量
- 图的逻辑结构(多对多)
图没有顺序存储结构,但可以结组二位数组来表示元素空间关系
链式存储结构:
多重链表·
邻接表
邻接多重表
十字链表
数组:链接矩阵表示法
链式: 邻接表
- 数组(邻接矩阵)表示法
建立一个顶点表(记录各个顶点信息)和一个邻接矩阵(表示各个顶点的关系)
2. 图的存储结构 1. 邻接矩阵· 1. 邻接矩阵
int MAXInt 极大值
int MAXNum 最大顶点数目
char VerTexTyoe 顶点的数据类型
int ArcType 假设边的权值类型为整型
邻接表二点数据结构类型
struct
VerTexType vexs[MAXnum]//顶点的数量
ArcType arcs[MAXNum][MAXNum] //邻接矩阵
int vexnum,arcnum;//图的当前的点数和变数
//AMGraph &G
- 怎么创建一个邻接矩阵
//采用邻接矩阵表示法,创建无向图G
Status CreateUDN(AMGraph $G){
//输入总的顶点数目,总边数
cin>> G.vexnum>>G.arcnum;
for(int = 0; G.vexnum; ++i)
//依次输入边的信息
cin>> G.vexs[i];
//初始化邻接矩阵
for (int i = 0; i>v1>>v2>>w;
i=LocateVex(G,V1);
j=LocateVex(G,V2);
G.arcs[i][j] = w;
G.arcs[j][i] = G.arcs[i][j];
}
return ok;
}
- LocateVex是在顶点表中查找顶点的下标
a. 建立顶点表和邻接矩阵的关系
b. 表示顶点和边的关系
设图A = (V,E)有个n个顶点,则顶点表如下:
i | 0 | 1 | 2 | … | n-1 |
---|---|---|---|---|---|
Vexs[i] | V1 | V2 | V3 | … | Vn |
邻接矩阵:
比如设置V2 和 V3 存在边的关系或者权值w
先去顶点表中查找V2的下标为 1,V3的下标的为2
设置邻接矩阵
arcs[1][2] = 1或者w;
arcs[2][1] = 1 或者w;
0 | 1 | 2 | … | n-1 | |
---|---|---|---|---|---|
0 | |||||
1 | 1/w | ||||
2 | 1/w | ||||
… | |||||
n-1 |
3.采用邻接矩阵表示法创建无向图总结:
1). 输入总顶点数和总边数
2). 依次输入点的信息存入顶点表中
3).初始化邻接矩阵,使每个权值初始化为极大值。
4).构造邻接矩阵。
- 邻接表表示法
- 顶点:
- 按照编号顺序将顶点数据存储在一维数组中
- 关联同一个点的边(以顶点为尾的弧)
- 用线性链表存储
- 无向图和有向图的邻接表的区别:
1. 无向图的邻接表不唯一
2. 若无向图中有n个顶点、e条边,则邻接表需要n个结点和2e个表节点,适合存储稀疏图。
- 图的邻接表存储表示
- 顶点的结构VNode
VerTexType(顶点的相关信息) | firstarc(第一个边结点坐标) |
---|
tpedef struct VNode{
//顶点的信息
VerTexType data;
//指向第一条依附该顶点的边指针
ArcNode * firstarc;
}VNode,AdjList[MVNum];//定义数组类型AdList为VNode的数组类型
- 边节点的定义和结构如下ArcNode:
adjvex(结点的下标位置) | nextarc(下一个边结点的指向) | info(边相关信息) |
---|
//最大顶点数目
#define MVN 100
//边节点结构
tyoedef struct ArcNode{
//指向顶点的位置
int adjvex;
//指向下一条边的指针
struct ArcNode *nextarc;
//和边相关的信息
OtgerInfo infol
}
- 邻接表表示的图的结构
typedef struct{
//node节点的列表
AdjList vertices;
//图的当前顶点数和弧数
int vexnum,arcnum;
}ALGraph;
- 设置图的相关参数
G.vexum = 5;G.arcnum = 5;
G.vertices[1].data='b';
p = G.vertices[1].firtarc;
p -> adjvex = 4;
- 怎么建立邻接表
- 算法思想如下
1. 输入总的顶点数和总边数
2. 建立顶点表
依次输入点的信息存入顶点表中
使每个表头结点的指针域初始化为null
3. 创建邻接表
依次输入每条边依附的两个顶点
确定两个顶点的序列号i和j,建立边结点
将此结点分别插入vi和vj对应的两个边链表的头部
- 采用邻接表创建无向网
Status CreateUDG(ALGraph &G){
//输入总的顶点数,总边数
cin>>G.vexnum>>G.arcnum;
//输入各点,构造表头结点表
for(int i = 0; i< G.vexnum;++i){
//输入顶点值
cin>> G.vertices[i].data;
//初始化表头结点的指针域
G.vertices[i].firstarc = NULL;
}
for(k= 0; G.arcnum; ++k){
//输入边的关系
cin>>v1>>v2;
//查找v1,v2在数组的中位置
i = LocateVex(G,v1);
j = LocateVex(G,v2);
p1 = new ArcNode;
p1-> adjvex = j;
p1-> nextarc = G.vertices[i].firstarc;
G.vertices[i].firstarc = p1;
//由于是无向的j的边也要指向这条边
p2 = new ArcNode;
p2->adjvex =i;
p2->nextarc = G.vertices[j].firstarc;
G.vertices[j].firstarc = p2;
}
return ok;
}
- 有向图和无向图的邻接表优缺点
有向图:
缺点:求结点的度困难
无向图:
缺点:每条边都要存储两边
3. 十字链表
- 顶点结点
data | firstin(入度边) | firstout(出度边) |
---|
- 弧结点
tailvex(弧尾结点坐标) | headvex(弧头结点坐标) | hlink(下一个弧头相同的弧) | tlink(下一个弧尾相同的弧) |
---|
- 示例
- 解决的问题
解决了无向图的每个边都要存储俩遍的问题
mark | ivex | inlink | jvex | jlink | info |
---|
- 遍历的定义:
从已经给的连通图的某一个顶点出发,沿着一些边访问图的所有的顶点,且使每个顶点仅被访问一次
- 图的特点:
图中可能存在的回路
- 怎么避免重复访问?
解决思路:设置辅助数组visited[n],用来标记每个被访问过的顶点。
初始化状态visited[i] 为 0;
顶点i被访问,改visited[i]为1,防止多次被访问
- 图常用的遍历方法:
- 深度优先遍历-DFS
- 广度优先遍历-BFS
- 深度优先算法的实现
- 邻接矩阵
1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|
1 | 0 | 1 | 1 | 1 | 0 | 0 |
2 | 1 | 0 | 0 | 0 | 1 | 0 |
3 | 1 | 0 | 0 | 0 | 1 | 0 |
4 | 1 | 0 | 0 | 0 | 0 | 1 |
5 | 0 | 1 | 1 | 0 | 0 | 0 |
6 | 0 | 0 | 0 | 1 | 0 | 0 |
- 采用邻接矩阵表示连通图的优先遍历
void DFS(AMGraph G,int v){
//访问第V个顶点
cout<
-
邻接矩阵的时间时间复杂度O(n^2)
-
邻接表的的时间复杂度O(n个头结点+e)
-
非连通图的遍历
- 连通图已经访问过的结点后
- 从未访问的结点的结点中获取开始结点进行遍历
- 广度优先遍历
1. v1 插入队列
2. v1 出队
3. 将v1的邻接点入队
....
- visited[i]
0 | 1 | 2 | 3 |
---|---|---|---|
- 广度优先的算法实现
void BFS(Graph G,int v){
cout<=0;w = NextAdjVex(G,u,w)){
//尚未访问的邻接顶点的结点入队
if(!visited[w]){
cout<< w;
visited[w] = true;
EnQueue(Q,w);
}
}
}
}
3. 图的应用
- 最小生成树
最小生成树:给定一个无向网,在该网的所有生成树中,使的各个边权值之和最小的那棵树称为最小生成树,最小代价生成树
- 构造最小生成树Minimum Spanning Tree
构造最小生成树的算法很多,利用MST性质
MST:
设 N = (V,E) 是一个连通网,U 是顶点集 V 的一个非空子集。
若边(u,v) 是一条具有最小权值的边,其中 u∈U, v∈V-U,则必存在一棵树包含(u,v)的最小生成树
在生成树的构造过程中,图中n个顶点分属两个集合:
1.已经落到生成树上的顶点集: U
2.尚未落到生成树上的顶点集: V - U
接下来则应在所有连通U中顶点和V - U 中顶点的边中选取权值最小的边
- Prim 算法
1.设N= (V,E)是连通网,TE是N上最小生成树中的边的集合。
2.初始令 U = {u0},(u0∈V),TE={}。
3.在所有u∈U,v∈V-U的边(u,v)∈E中,找一条代价最小的边(u0,v0).
- 克鲁斯卡尔(KrusKal)算法
- 最短路径
- Dijistra算法
1. 初始化: 先找出从源点V0到各终点的vk的直达路径(v0,vk),即通过一条弧到达的路径
2. 选择: 从这些路径中找出一条长度最短的路径(v0,u)
3. 更新:然后对其余各条路径进行调整:
若在图中存在弧(u,vk),且(v0,u)+(u,vk)<(v0,vk)
则以路径(v0,u,vk)代替(v0,vk)。
V - U
接下来则应在所有连通U中顶点和V - U 中顶点的边中选取权值最小的边
* Prim 算法
1.设N= (V,E)是连通网,TE是N上最小生成树中的边的集合。
2.初始令 U = {u0},(u0∈V),TE={}。
3.在所有u∈U,v∈V-U的边(u,v)∈E中,找一条代价最小的边(u0,v0).
[外链图片转存中...(img-e6Vytsap-1649150426788)]
* 克鲁斯卡尔(KrusKal)算法
[外链图片转存中...(img-ouGGtuXe-1649150426789)]
2. 最短路径
[外链图片转存中...(img-Twk37u3P-1649150426789)]
[外链图片转存中...(img-v482b8tS-1649150426789)]
* Dijistra算法
- 初始化: 先找出从源点V0到各终点的vk的直达路径(v0,vk),即通过一条弧到达的路径
- 选择: 从这些路径中找出一条长度最短的路径(v0,u)
- 更新:然后对其余各条路径进行调整:
若在图中存在弧(u,vk),且(v0,u)+(u,vk)<(v0,vk)
则以路径(v0,u,vk)代替(v0,vk)。
- DFS-Java代码实现如下
package com.lj.advance;
import java.util.Scanner;
public class DFS {
static final int MAXINT = Integer.MAX_VALUE;
static final int MAX_NUM = 233;
static int[] visited = new int[MAX_NUM];
static class AMGraph {
//顶点集合
String[] vexes = new String[MAX_NUM];
//邻接矩阵
int[][] arcs = new int[MAX_NUM][MAX_NUM];
//顶点数
int vexNum;
//边数
int arcNum;
}
/**
* 初始化邻接矩阵
* @param G
* @return
*/
static boolean createUDN(AMGraph G){
Scanner sc = new Scanner(System.in);
int vexNum = sc.nextInt();
int arcNum = sc.nextInt();
G.vexNum = vexNum;
G.arcNum = arcNum;
sc = new Scanner(System.in);
for (int i= 0 ;i
- BFS-代码实现如下
package com.lj.advance;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class BFS {
public static final int MAX_NUM = 100;
public static int[] visited = new int[MAX_NUM];
/**
* 顶点结构
*/
static class VNode {
//顶点的数据信息
String data;
//边结构依赖坐标指向
ArcNode firstArc;
}
/**
* 边结构
*/
static class ArcNode {
//指向的顶点位置
int adjVex;
//指向下一条边的指针
ArcNode nextArc;
}
/**
* 设置图的结构
*/
static class ArcGraph {
//顶点的列表
VNode[] vertices = new VNode[MAX_NUM];
//图的当前顶点数
int vexNum;
//图的当前的边数
int arcNum;
}
static boolean createUDG(ArcGraph G) {
Scanner sc = new Scanner(System.in);
//设置图的顶点数目
G.vexNum = sc.nextInt();
//设置图的边数
G.arcNum = sc.nextInt();
sc = new Scanner(System.in);
//构造顶点列表结构
for (int i = 0; i < G.vexNum; i++) {
VNode vNode = new VNode();
vNode.data = sc.next();
vNode.firstArc = null;
G.vertices[i] = vNode;
}
//构造边结构
for (int k = 0; k < G.arcNum; k++) {
//输入边的关系
sc = new Scanner(System.in);
String v1 = sc.next();
String v2 = sc.next();
//点位边的下标索引
int i = LocateVex(G, v1);
int j = LocateVex(G, v2);
ArcNode nodeI = new ArcNode();
nodeI.adjVex = j;
nodeI.nextArc = G.vertices[i].firstArc;
G.vertices[i].firstArc = nodeI;
ArcNode nodeJ = new ArcNode();
nodeJ.adjVex = i;
nodeJ.nextArc = G.vertices[j].firstArc;
G.vertices[j].firstArc = nodeJ;
}
return true;
}
static void bfs(ArcGraph G, int v) {
System.out.println("访问第" + v + "顶点");
visited[v] = 1;
Queue queue = new LinkedList<>();
//将该顶点入队列
queue.add(v);
while (!queue.isEmpty()) {
Integer index = queue.poll();
for (int w = firstAdjVex(G,index);w>=0;w = nextAdjVex(G,index,w)){
if(visited[w]==0){
System.out.println("访问第" + w + "顶点");
visited[w] = 1;
queue.add(w);
}
}
}
}
static int nextAdjVex(ArcGraph G,int u, int index){
ArcNode p = G.vertices[u].firstArc;
ArcNode node = null;
while (p!=null){
int adjVex = p.adjVex;
if(adjVex==index){
node = p;
}
p = p.nextArc;
}
if(node!=null&&node.nextArc!=null){
return node.nextArc.adjVex;
}else {
return -1;
}
}
static int firstAdjVex(ArcGraph G, int u) {
ArcNode firstArc = G.vertices[u].firstArc;
if (firstArc != null) {
return firstArc.adjVex;
} else {
return -1;
}
}
static int LocateVex(ArcGraph G, String v) {
for (int i = 0; i < G.vexNum; i++) {
String data = G.vertices[i].data;
if (v.equals(data)) {
return i;
}
}
return -1;
}
public static void main(String[] args) {
ArcGraph graph = new ArcGraph();
createUDG(graph);
VNode[] vertices = graph.vertices;
for (int i = 0; i < graph.vexNum; i++) {
System.out.print("vex" + vertices[i].data + ":" + "\t");
ArcNode p = vertices[i].firstArc;
while (p != null) {
System.out.print("next adj " + p.adjVex + "\t");
p = p.nextArc;
}
System.out.println();
}
for (int i = 0;i
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)