第 13 章图

文章目录

  • 13.1 图基本介绍
    • 13.1.1 为什么要有图
    • 13.1.2 图的举例说明
  • 13.2 图的表示方式
    • 13.2.1 邻接矩阵
    • 13.2.2 邻接表
  • 13.3 图的快速入门案例
  • 13.4 图的深度优先遍历介绍
    • 13.4.1 图遍历介绍
    • 13.4.2 深度优先遍历基本思想
    • 13.4.3 深度优先遍历算法步骤
    • 13.4.4 深度优先算法的代码实现
  • 13.5 图的广度优先遍历
    • 13.5.1 广度优先遍历基本思想
    • 13.5.2 广度优先遍历算法步骤
    • 13.5.3 广度优先算法的图示
  • 13.6 广度优先算法的代码实现
  • 13.7 图的代码汇总
  • 13.8 图的深度优先VS 广度优先

13.1 图基本介绍

13.1.1 为什么要有图

  1. 前面我们学了线性表和树
  2. 线性表局限于一个直接前驱和一个直接后继的关系
  3. 树也只能有一个直接前驱也就是父节点
  4. 当我们需要表示多对多的关系时, 这里我们就用到了图。

13.1.2 图的举例说明

图是一种数据结构,其中结点可以具有零个或多个相邻元素。两个结点之间的连接称为边。 结点也可以称为顶点。如图:
在这里插入图片描述

13.1.3 图的常用概念

  1. 顶点(vertex)

  2. 边(edge)

  3. 路径

  4. 无向图(右图
    在这里插入图片描述

  5. 有向图

  6. 带权图
    在这里插入图片描述

13.2 图的表示方式

图的表示方式有两种:二维数组表示(邻接矩阵);链表表示(邻接表)。

13.2.1 邻接矩阵

邻接矩阵是表示图形中顶点之间相邻关系的矩阵,对于 n 个顶点的图而言,矩阵是的 row 和 col 表示的是 1 n
个点。
在这里插入图片描述

13.2.2 邻接表

  1. 邻接矩阵需要为每个顶点都分配 n 个边的空间,其实有很多边都是不存在,会造成空间的一定损失.
  2. 邻接表的实现只关心存在的边,不关心不存在的边。因此没有空间浪费,邻接表由数组+链表组成
  3. 举例说明

13.3 图的快速入门案例

  1. 要求: 代码实现如下图结构.
    在这里插入图片描述

  2. 思路分析
    (1) 存储顶点 String 使用 ArrayList
    (2) 保存矩阵 int[][] edges

  3. 代码实现

     //插入结点
    	public void insertVertex(String vertex) {
    		vertexList.add(vertex);
    	}
    	//添加边
    	/**
    	 * 
    	 * @param v1 表示点的下标即使第几个顶点  "A"-"B" "A"->0 "B"->1
    	 * @param v2 第二个顶点对应的下标
    	 * @param weight 表示 
    	 */
    	public void insertEdge(int v1, int v2, int weight) {
    		edges[v1][v2] = weight;
    		edges[v2][v1] = weight;
    		numOfEdges++;
    	}
    

13.4 图的深度优先遍历介绍

13.4.1 图遍历介绍

所谓图的遍历,即是对结点的访问。一个图有那么多个结点,如何遍历这些结点,需要特定策略,一般有两种访问策略: (1)深度优先遍历 (2)广度优先遍历

13.4.2 深度优先遍历基本思想

图的深度优先搜索(Depth First Search) 。

  1. 深度优先遍历,从初始访问结点出发,初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点, 可以这样理解:每次都在访问完当前结点后首先访问当前结点的第一个邻接结点。
  2. 我们可以看到,这样的访问策略是优先往纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问。
  3. 显然,深度优先搜索是一个递归的过程

13.4.3 深度优先遍历算法步骤

  1. 访问初始结点 v,并标记结点 v 为已访问。
  2. 查找结点 v 的第一个邻接结点 w。
  3. 若 w 存在,则继续执行 4,如果 w 不存在,则回到第 1 步,将从 v 的下一个结点继续。
  4. 若 w 未被访问,对 w 进行深度优先遍历递归(即把 w 当做另一个 v,然后进行步骤 123)。
  5. 查找结点 v 的 w 邻接结点的下一个邻接结点,转到步骤 3。
  6. 分析图

在这里插入图片描述

13.4.4 深度优先算法的代码实现

 //深度优先遍历算法
	//i 第一次就是 0
	private void dfs(boolean[] isVisited, int i) {
		//首先我们访问该结点,输出
		System.out.print(getValueByIndex(i) + "->");
		//将结点设置为已经访问
		isVisited[i] = true;
		//查找结点i的第一个邻接结点w
		int w = getFirstNeighbor(i);
		while(w != -1) {//说明有
			if(!isVisited[w]) {
				dfs(isVisited, w);
			}
			//如果w结点已经被访问过
			w = getNextNeighbor(i, w);
		}
		
	}
	
	//对dfs 进行一个重载, 遍历我们所有的结点,并进行 dfs
	public void dfs() {
		isVisited = new boolean[vertexList.size()];
		//遍历所有的结点,进行dfs[回溯]
		for(int i = 0; i < getNumOfVertex(); i++) {
			if(!isVisited[i]) {
				dfs(isVisited, i);
			}
		}
	}

13.5 图的广度优先遍历

13.5.1 广度优先遍历基本思想

  1. 图的广度优先搜索(Broad First Search) 。
  2. 类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的结点的顺序,以便按这个顺序来访问这些结点的邻接结点

13.5.2 广度优先遍历算法步骤

  1. 访问初始结点 v 并标记结点 v 为已访问。

  2. 结点 v 入队列

  3. 当队列非空时,继续执行,否则算法结束。

  4. 出队列,取得队头结点 u。

  5. 查找结点 u 的第一个邻接结点 w。

  6. 若结点 u 的邻接结点 w 不存在,则转到步骤 3;否则循环执行以下三个步骤:

  • 若结点 w 尚未被访问,则访问结点 w 并标记为已访问。
  • 结点 w 入队列
  • 查找结点 u 的继 w 邻接结点后的下一个邻接结点 w,转到步骤 6。

13.5.3 广度优先算法的图示

在这里插入图片描述

13.6 广度优先算法的代码实现

	//对一个结点进行广度优先遍历的方法
		private void bfs(boolean[] isVisited, int i) {
			int u ; // 表示队列的头结点对应下标
			int w ; // 邻接结点w
			//队列,记录结点访问的顺序
			LinkedList queue = new LinkedList();
			//访问结点,输出结点信息
			System.out.print(getValueByIndex(i) + "=>");
			//标记为已访问
			isVisited[i] = true;
			//将结点加入队列
			queue.addLast(i);
			
			while( !queue.isEmpty()) {
				//取出队列的头结点下标
				u = (Integer)queue.removeFirst();
				//得到第一个邻接结点的下标 w 
				w = getFirstNeighbor(u);
				while(w != -1) {//找到
					//是否访问过
					if(!isVisited[w]) {
						System.out.print(getValueByIndex(w) + "=>");
						//标记已经访问
						isVisited[w] = true;
						//入队
						queue.addLast(w);
					}
					//以u为前驱点,找w后面的下一个邻结点
					w = getNextNeighbor(u, w); //体现出我们的广度优先
				}
			}
			
		} 
		
		//遍历所有的结点,都进行广度优先搜索
		public void bfs() {
			isVisited = new boolean[vertexList.size()];
			for(int i = 0; i < getNumOfVertex(); i++) {
				if(!isVisited[i]) {
					bfs(isVisited, i);
				}
			}
		}

13.7 图的代码汇总

	 package com.atguigu.graph;
	
	import java.util.ArrayList;
	import java.util.Arrays;
	import java.util.LinkedList;
	
	public class Graph {
	
		private ArrayList<String> vertexList; //存储顶点集合
		private int[][] edges; //存储图对应的邻结矩阵
		private int numOfEdges; //表示边的数目
		//定义给数组boolean[], 记录某个结点是否被访问
		private boolean[] isVisited;
		
		public static void main(String[] args) {
			//测试一把图是否创建ok
			int n = 8;  //结点的个数
			//String Vertexs[] = {"A", "B", "C", "D", "E"};
			String Vertexs[] = {"1", "2", "3", "4", "5", "6", "7", "8"};
			
			//创建图对象
			Graph graph = new Graph(n);
			//循环的添加顶点
			for(String vertex: Vertexs) {
				graph.insertVertex(vertex);
			}
			
			//添加边
			//A-B A-C B-C B-D B-E 
	//		graph.insertEdge(0, 1, 1); // A-B
	//		graph.insertEdge(0, 2, 1); // 
	//		graph.insertEdge(1, 2, 1); // 
	//		graph.insertEdge(1, 3, 1); // 
	//		graph.insertEdge(1, 4, 1); // 
			
			//更新边的关系
			graph.insertEdge(0, 1, 1);
			graph.insertEdge(0, 2, 1);
			graph.insertEdge(1, 3, 1);
			graph.insertEdge(1, 4, 1);
			graph.insertEdge(3, 7, 1);
			graph.insertEdge(4, 7, 1);
			graph.insertEdge(2, 5, 1);
			graph.insertEdge(2, 6, 1);
			graph.insertEdge(5, 6, 1);
	
			
			
			//显示一把邻结矩阵
			graph.showGraph();
			
			//测试一把,我们的dfs遍历是否ok
			System.out.println("深度遍历");
			graph.dfs(); // A->B->C->D->E [1->2->4->8->5->3->6->7]
	//		System.out.println();
			System.out.println("广度优先!");
			graph.bfs(); // A->B->C->D-E [1->2->3->4->5->6->7->8]
			
		}
		
		//构造器
		public Graph(int n) {
			//初始化矩阵和vertexList
			edges = new int[n][n];
			vertexList = new ArrayList<String>(n);
			numOfEdges = 0;
			
		}
		
		//得到第一个邻接结点的下标 w 
		/**
		 * 
		 * @param index 
		 * @return 如果存在就返回对应的下标,否则返回-1
		 */
		public int getFirstNeighbor(int index) {
			for(int j = 0; j < vertexList.size(); j++) {
				if(edges[index][j] > 0) {
					return j;
				}
			}
			return -1;
		}
		//根据前一个邻接结点的下标来获取下一个邻接结点
		public int getNextNeighbor(int v1, int v2) {
			for(int j = v2 + 1; j < vertexList.size(); j++) {
				if(edges[v1][j] > 0) {
					return j;
				}
			}
			return -1;
		}
		
		//深度优先遍历算法
		//i 第一次就是 0
		private void dfs(boolean[] isVisited, int i) {
			//首先我们访问该结点,输出
			System.out.print(getValueByIndex(i) + "->");
			//将结点设置为已经访问
			isVisited[i] = true;
			//查找结点i的第一个邻接结点w
			int w = getFirstNeighbor(i);
			while(w != -1) {//说明有
				if(!isVisited[w]) {
					dfs(isVisited, w);
				}
				//如果w结点已经被访问过
				w = getNextNeighbor(i, w);
			}
			
		}
		
		//对dfs 进行一个重载, 遍历我们所有的结点,并进行 dfs
		public void dfs() {
			isVisited = new boolean[vertexList.size()];
			//遍历所有的结点,进行dfs[回溯]
			for(int i = 0; i < getNumOfVertex(); i++) {
				if(!isVisited[i]) {
					dfs(isVisited, i);
				}
			}
		}
		
		//对一个结点进行广度优先遍历的方法
		private void bfs(boolean[] isVisited, int i) {
			int u ; // 表示队列的头结点对应下标
			int w ; // 邻接结点w
			//队列,记录结点访问的顺序
			LinkedList queue = new LinkedList();
			//访问结点,输出结点信息
			System.out.print(getValueByIndex(i) + "=>");
			//标记为已访问
			isVisited[i] = true;
			//将结点加入队列
			queue.addLast(i);
			
			while( !queue.isEmpty()) {
				//取出队列的头结点下标
				u = (Integer)queue.removeFirst();
				//得到第一个邻接结点的下标 w 
				w = getFirstNeighbor(u);
				while(w != -1) {//找到
					//是否访问过
					if(!isVisited[w]) {
						System.out.print(getValueByIndex(w) + "=>");
						//标记已经访问
						isVisited[w] = true;
						//入队
						queue.addLast(w);
					}
					//以u为前驱点,找w后面的下一个邻结点
					w = getNextNeighbor(u, w); //体现出我们的广度优先
				}
			}
			
		} 
		
		//遍历所有的结点,都进行广度优先搜索
		public void bfs() {
			isVisited = new boolean[vertexList.size()];
			for(int i = 0; i < getNumOfVertex(); i++) {
				if(!isVisited[i]) {
					bfs(isVisited, i);
				}
			}
		}
		
		//图中常用的方法
		//返回结点的个数
		public int getNumOfVertex() {
			return vertexList.size();
		}
		//显示图对应的矩阵
		public void showGraph() {
			for(int[] link : edges) {
				System.err.println(Arrays.toString(link));
			}
		}
		//得到边的数目
		public int getNumOfEdges() {
			return numOfEdges;
		}
		//返回结点i(下标)对应的数据 0->"A" 1->"B" 2->"C"
		public String getValueByIndex(int i) {
			return vertexList.get(i);
		}
		//返回v1和v2的权值
		public int getWeight(int v1, int v2) {
			return edges[v1][v2];
		}
		//插入结点
		public void insertVertex(String vertex) {
			vertexList.add(vertex);
		}
		//添加边
		/**
		 * 
		 * @param v1 表示点的下标即使第几个顶点  "A"-"B" "A"->0 "B"->1
		 * @param v2 第二个顶点对应的下标
		 * @param weight 表示 
		 */
		public void insertEdge(int v1, int v2, int weight) {
			edges[v1][v2] = weight;
			edges[v2][v1] = weight;
			numOfEdges++;
		}
	}

13.8 图的深度优先VS 广度优先

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/320821.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

4D 毫米波雷达:智驾普及的新路径(二)

4 4D 毫米波的技术路线探讨 4.1 前端收发模块 MMIC&#xff1a;级联、CMOS、AiP 4.1.1 设计&#xff1a;级联、单芯片、虚拟孔径 4D 毫米波雷达的技术路线主要分为三种&#xff0c;分别是多级联、级联 虚拟孔径成像技术、以及 集成芯片。&#xff08; 1 &#xff09;多级…

跟着暄桐林曦老师读《宝贵的人生建议》,重视心这颗种子

暄桐林曦老师在《见道明心的笔墨》读书课上讲到&#xff1a;人要在心这颗种子上去进化。当人的动机和果实都清静时&#xff0c;才能在内心具足里转化出更多可能性&#xff0c;进入正面的循环里。“宽以待人&#xff0c;严以律己&#xff0c;反之&#xff0c;则人人身处地狱”&a…

基于Java SSM框架实现体育竞赛成绩管理系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现体育竞赛成绩管理系统演示 摘要 体育竞赛是各种体育体育项目比赛的总称。是在裁判员的主持下&#xff0c;按统一的规则要求&#xff0c;组织与实施的体育员个体或体育队之间的竞技较量&#xff0c;是竞技体育与社会发生关联&#xff0c;并作用于社会的媒…

Javascript——vue下载blob文档流

<el-table-column label"操作" fixed"right" width"150" showOverflowTooltip><template slot-scope"scope"><el-button type"text" v-has"stbsd-gjcx-down" class"edit-button" click&…

超详细的搭建压测平台笔记

0、前言 最近重新回来学习熊哥的极客教程&#xff0c;结合自己学习的shell编程和Docker的指令学习&#xff0c;对熊哥的一些操作做bash脚本自动化&#xff0c;将搭建压测平台的步骤做记录&#xff0c;目的是分享搭建过程。 过程中会安装docker,mysql,redis,influxdb,grafana,…

python 通过定时任务执行pytest case

这段Python代码使用了schedule库来安排一个任务&#xff0c;在每天的22:50时运行。这个任务执行一个命令来运行pytest&#xff0c;并生成一个报告。 代码开始时将job_done变量设为False&#xff0c;然后运行预定的任务。一旦任务完成&#xff0c;将job_done设置为True并跳出循…

基于Java SSM框架实现学生成绩管理系统项目【项目源码+论文说明】

基于java的SSM框架实现学生成绩管理系统演示 摘要 学生成绩是高校人才培养计划的重要组成部分&#xff0c;是实现人才培养目标、培养学生科研能力与创新思维、检验学生综合素质与实践能力的重要手段与综合性实践教学环节。而学生所在学院多采用半手工管理学生成绩的方式&#…

【PyTorch简介】3.Loading and normalizing datasets 加载和规范化数据集

Loading and normalizing datasets 加载和规范化数据集 文章目录 Loading and normalizing datasets 加载和规范化数据集Datasets & DataLoaders 数据集和数据加载器Loading a Dataset 加载数据集Iterating and Visualizing the Dataset 迭代和可视化数据集Creating a Cust…

如何使用csdn中的c知道进行学习?

1.c知道 猜测是通过chatgpt训练链接到CSDN内部的文章内容等&#xff0c;进行生成的一款应用。 2.如何使用呢 打比方说&#xff0c;我想学习下多目标跟踪中的ukf&#xff0c;那么就可以输入这个关键字。 那既然是学习&#xff0c;就要进一步深究&#xff0c;有三种方式&#…

LV.13 D11 Linux驱动移植及内核深化 学习笔记

一、设备树 1.1 设备树 设备树是一种描述硬件信息的数据结构&#xff0c;Linux内核运行时可以通过设备树将硬件信息直接传递给Linux内核&#xff0c;而不再需要在Linux内核中包含大量的冗余编码 举例&#xff1a;让LED2闪烁的代码中&#xff0c;有逻辑代码和设备代码。Li…

Django数据库选移的preserve_default=False是什么意思?

有下面的迁移命令&#xff1a; migrations.AddField(model_namemovie,namemov_group,fieldmodels.CharField(defaultdjango.utils.timezone.now, max_length30),preserve_defaultFalse,),迁移命令中的preserve_defaultFalse是什么意思呢&#xff1f; 答&#xff1a;如果模型定…

点击随机红点的简单游戏(pygame)

import pygame import sys import random# 初始化 Pygame pygame.init()# 设置窗口大小 width, height 800, 600 screen pygame.display.set_mode((width, height)) pygame.display.set_caption("Click the Red Dot")# 定义颜色 black (0, 0, 0) red (255, 0, 0)…

Spring来了,春天还会远吗?

结束了JVM的学习后&#xff0c;要进入的是JavaEE进阶的学习了。JavaEE进阶学习内容很多很丰富&#xff0c;并且也很有难度。今天我们就从Spring开始讲起。 目录 框架的好处 怎么学框架 Spring核心与设计思想 容器 IoC Spring IoC DI&#xff08;Dependency Injection&a…

Android 通知

通知 通知是指 Android 在应用界面之外显示的消息&#xff0c;旨在向用户提供提醒、来自他人的通信或应用中的其他实时信息。用户可以点按通知以打开您的应用&#xff0c;或直接从通知中执行操作。 通知在状态栏中显示为图标&#xff0c;在抽屉式通知栏中显示更详细的条目&…

【书生·浦语】大模型实战营——第五课作业

教程文档&#xff1a;https://github.com/InternLM/tutorial/blob/vansin-patch-4/lmdeploy/lmdeploy.md#tritonserver-%E6%9C%8D%E5%8A%A1%E4%BD%9C%E4%B8%BA%E5%90%8E%E7%AB%AF 视频链接&#xff1a; 作业&#xff1a; 基础作业 使用如下命令创建conda环境 conda create…

【期末考试】数据库综合复习宝典

目录 第一章 数据库系统概述 第二章 关系代数 第四章 关系数据库理论 第五章 数据库设计 第六章 数据库管理系统 第八章 事务管理 第一章 数据库系统概述 1.1三级模式 ①外模式&#xff1a;它为特定的应用程序或用户群体提供了一个数据视图&#xff0c;这个视图是独立于…

评估LLM在细胞数据上的实用性(2)-细胞层面的评估

本文衔接上一篇&#xff1a;评估LLM在细胞数据上的实用性(1)-基本概述 目录 定义参数和任务批次整合多模态整合细胞类型注释 细胞层面的评估批次整合多模态整合细胞类型注释 定义 我们考虑一个预训练LLM表示为 M ( x , θ ) M(x,\theta) M(x,θ)&#xff0c;其基于单细胞数据…

设计模式-传输对象模式

设计模式专栏 模式介绍模式特点应用场景传输对象模式和序列化的区别代码示例Java实现传输对象模式Python实现传输对象模式 传输对象模式在spring中的应用 模式介绍 传输对象模式&#xff08;Transfer Object Pattern&#xff09;是一种设计模式&#xff0c;用于从客户端向服务…

【redis基础1】基础数据类型详解和应用案例

博客计划 &#xff0c;我们从redis开始&#xff0c;主要是因为这一块内容的重要性不亚于数据库&#xff0c;但是很多人往往对redis的问题感到陌生&#xff0c;所以我们先来研究一下。 本篇&#xff0c;我们先看一下redis的基础数据类型详解和应用案例。 1.redis概述 以mysql为…

某mps政务网站jsl加速乐cookie逆向解析

本文针对的目标网站如下&#xff0c;使用base64解密获得 aHR0cHM6Ly93d3cubXBzLmdvdi5jbi8 开篇&#xff1a;加速乐其实算是比较好处理的逆向问题了&#xff0c;了解过的朋友都知道&#xff0c;其本身跟瑞数相似&#xff0c;都是设置cookie后才能成功&#xff0c;设置cookie的过…