算法和数据结构(10)图1

图(Graph)

表示多对多的关系

复习:

  1. 线性表 1对1的关系
  2. 树 1对多的关系
  3. 图 多对多的关系

在这里插入图片描述
上述即是一个图的理解模型;

图的定义和术语:

  1. 一个图(G)定义为一个耦对(V,E),记为G=(V,E);其中V是顶点(Vertex)的非空有限集合,记为V(G);E是边的集合,记为E(G);
  2. 理解:如上图,每个村庄为一个结点 ,即每个村庄为一个顶点,边表示顶点和顶点之间的关系,即引出了单向和双向的问题; 无向边,顶点和顶点之间互通;
    1. 边是顶点对,如(v,w)属于E,其中v,w属于v。()表示无向边,即顶点v和顶点w互通;
    2. 有向边<v,w>,表示v->w,v能走到w,w不能走到v,其中 <v,w>属于E,v,w属于V;
    3. 图不考虑重边(只有一条边),和自回路(自己指向自己);
  3. 综上述,图有两部分组成顶点和边;V是非空的有限顶点的集合,E是一个有限边的集合; 记为G(V,E)

术语

  1. 无向图,图中的所有边都是无向的,则称为无向图;

  2. 有向图 ,图中的部分边是有向的,即边的方向很重要,官方定义:图中顶点偶对<v,w>的v,w之间是有序的,称图G是有向图;

  3. 图中每条边加了权重(例如距离,耗费等 ),则把此图称为网络

    更多术语。。。持续更新;

程序表示图

邻接矩阵

用二位数组表示图

g[n][n], n表示n个顶点的编号
若g[i][j] = 1 // 表示i和j 连接有边
若g[i][j] = 0 // 表示i和j 无边

g[0][1] = 1  // 表示0和1直接有单向边
g[1][0] = 1//表示1和0有单向边,
               //若g[0][1]=g[1][0]=1,则1和0之间无向边;
g[0][2]=0   //表示0和2直接无边

如下图;
在这里插入图片描述

在这里插入图片描述
不允许自回路,故:

对角线为轴,对称的,无向图
在这里插入图片描述
问题:对于无向图,一半空间实际上是内存浪费;

解决办法
在这里插入图片描述
在这里插入图片描述

  1. 只存一半,用一维数组存储这个图;
  2. 1,2,3…n 求和公式 n(n+1)/2;故上图中g[i][j] 在一维数组中对应的下标为 i*(i+1)/2 +j

邻接矩阵

优点:

  1. 直观,好理解
  2. 方便查找任意两顶点是否存在边
  3. 方便查找一顶点的所有边;
  4. 方便计算这个顶点的度,(出度:从这个顶点出去的边的数量,入度:指向这个顶点的边的个数 )

缺点:

  1. 浪费空间,稀疏的图,空间利用率差,对于稠密图,完全图(任意两个不同的顶点间都有一条边,又细分为完全有向图,完全无向图 )就很合算,空间利用率高;
  2. 浪费时间,稀疏图–统计有多少个边;

邻接表

在这里插入图片描述

真的很省内存空间吗?

  1. 对于无向图,实际都存了2个边
  2. 链表中还存有地址
  3. 对于网络中,结构中还需要加权重;

故:对于邻接表来说,一定要够稀疏才合算

优点:

  1. 方便找一个顶点的所有的邻接点
  2. 节约稀疏图的空间
    1. 需要n个头指针+2e个结点(详见上图)
  3. 方便计算仁一个结点的度?
    1. 对于无向图来说是。
    2. 对于有向图,不是;
    3. 出度易,入度有向图难

图的表示方法有很多种 非上述2种;

优化这个缺点,思想类似于线性表的思想,从数组转换为链表—邻接表表示法

图的遍历

DFS(Depth First Search)深度优先搜索

在这里插入图片描述
问题描述,从亮的那盏灯开始,如何点亮所有的灯
核心思想,递归;

//类似于树的先序遍历;

//伪代码
public void dfs(Vertex v){
  v.visited = true; //或者引入map等存储已访问的数据,设置v被防伪过
 	for(x的邻接点 w:v){
 		if(!w.visited){
 			dfs(w);
 		}
 	}
}

若图里有n个顶点,e条边,则时间复杂度为?

  1. 用邻接表表示 O(n+e);
  2. 用邻接矩阵表示,O(n2)

BFS(Breadth First Search) 广度优先搜索

在树中,类似于层序遍历;见树的博客:https://blog.csdn.net/qq_32193775/article/details/104031481,

https://blog.csdn.net/qq_32193775/article/details/104107629;

回忆:在这里插入图片描述
在这里插入图片描述

//伪代码如下
public void traversal(Tree tree){
	if(tree!=null){
		Queue que = Queue.getQueue(size);
		que.add(tree);
		while(!que.isEmpty()){
			Tree treeTemp = que.pop();
			System.out.println(treeTemp.data);
      if(tree.left!=null)
			que.add(tree.left);
			if(tree.right!=null)
			que.add(tree.right);
		}
	}
}

图中的BFS伪代码描述:
在这里插入图片描述
在这里插入图片描述

public void bfs(Vertex v){
	Queue que = new Queue();
	v.visited = true;
	que.add(v);
	while(!que.isEmpty){
    Vertex v2 = que.pop();
		for(v2的邻接点 w){	
			que.add(w);
			w.visited = true;
		}
	}
}

若n个顶点,e条边,时间复杂度为?

  1. 邻接表 O(n+e)
  2. 邻接矩阵O(n2)

广度优先和深度优先的区别

  1. 深度优先是优先结点的子结点,处理完这些子结点后返回,直接深入该结点的子孙结点;

  2. 广度优先是利用了栈,在一圈一圈的处理,利用栈的先后顺序;在广度上逐步深入;

发布了17 篇原创文章 · 获赞 0 · 访问量 362

猜你喜欢

转载自blog.csdn.net/qq_32193775/article/details/104133695