第五周作业 -- 有向图邻接表表示及反向图构造

本文介绍了如何实现有向图及其反向图的邻接表表示,包括读取文件、构建邻接表、输出邻接表及反向邻接表,并对反向邻接表中的顶点指向进行排序。

本次代码实现有向图及其反向图的邻接表显示。采用方式:使用二位数组保存从文档读出的每行数据,并在读取时进行反向保存反向图的数据。由于不确定某个顶点所指向的子顶点的个数,因此采取了ArrayList<Integer>的方式保存指向顶点的值。为了能够按照数字的顺序输出,将ArrayList<Integer>保存的值转存为int数组,传入数组进行冒泡排序完成。


代码实现:

/**
 * 1.有向图的邻接表表示
 * 2.有向图的反转
 * @author pan 
 * @date 2014-4-8
 */

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class GraphReverse {
	
	/**
	 * @param args[0] 指定文件名,需指定文件类型
	 * @throws FileNotFoundException 
	 */
	public static void main(String... args) throws FileNotFoundException{
		
		//获取源文件中边边关系数目
		GraphReverse gr = new GraphReverse();
		final int count = gr.lineCout("txt/tinyDG.txt", 2);//args[0]
		
		//定义读取文件对象,读取顶点数(需要显示的邻接表行数)
		Scanner scan = new Scanner(new File("txt/tinyDG.txt"));//args[0]
		final int vertex = scan.nextInt();
		
		//定义二维数组,保存读取的顶点之间的连接关系
		int[][] lineData = new int[count][2];
		int[][] reverseLineData = new int[count][2];
		//跳过开始两行
		scan.nextLine();
		scan.nextLine();
		
		//二维数组,初始化二维数组,按源文件顺序保存顶点之间的连接关系
		int line = 0;
		while(scan.hasNextInt()){
			lineData[line][0] = scan.nextInt();
			lineData[line][1] = scan.nextInt();
			reverseLineData[line][1] = lineData[line][0];
			reverseLineData[line][0] = lineData[line][1];
			line++;
		}
		scan.close();
		
		//输出原图的邻接表
		String str = "1.有向图的邻接表如下:";
		System.err.println(str);
		outGraph(count, vertex, lineData);
		
		//输出反向图的邻接表
		str = "\n2.反向图的邻接表如下:";
		System.err.println(str);
		outGraph(count, vertex, reverseLineData);
	}

	/**
	 * @param count 顶点关系数目
	 * @param vertex 顶点个数
	 * @param lineData 顶点关系
	 */
	private static void outGraph(final int count, final int vertex,
			int[][] lineData) {
		
		//保存顶点指向的顶点数值
		List<Integer> list = new ArrayList<>();
		//判断顶点关系中第一个顶点的值是否与顶点输出序列值相同
		//若相同则输出该顶点指向的顶点值
		for(int n=0;n<vertex;n++){
			System.out.print(n+": ");
			for(int i=0;i<count;i++){
				if(n==lineData[i][0]){
					list.add(lineData[i][1]);//把指向的顶点数值放入list中
				}	
			}
			
			//定义一个等于已有list元素长度的数组
			int elementCount = list.size();
			int[] arraySort = new int[elementCount];
			for(int j=0;j<elementCount;j++){
				arraySort[j] = (int)list.get(j);
			}
			list.clear();
			//排序数组,以实现按数字顺序输出
			GraphReverse gr = new GraphReverse();
			arraySort = gr.sortArray(arraySort);
			for(int j=0;j<elementCount;j++){
				System.out.print(arraySort[j]+" ");
			}	
			System.out.println();
		}
	}
	
	/**
	 * @param filePath 源文件路径
	 * @param n 文件头忽略的行数
	 * @return 返回顶点的边边关系数目
	 */
	public int lineCout(String filePath,int n) throws FileNotFoundException{
		int count=0;
		Scanner scanTxt = new Scanner(new File(filePath));
		while(scanTxt.hasNextLine()){
			//读取指针指向下一行
			scanTxt.nextLine();
			count++;
		}
		scanTxt.close();
		return count-n;
	}
	
	//传入数组进行冒泡排序
	public int[] sortArray(int[] array){
		for(int i=1,n=array.length;i<n;i++)
			for(int j=0;j<n-i;j++){
				if(array[j]>array[j+1]){
					int t = array[j];
					array[j] = array[j+1];
					array[j+1] = t;
			}
		}
		return array;
	}
}


运行结果:


<think>首先,用户的问题是关于使用邻接链表定义时需要定义的属性与方法。用户提供了参考引用,这些引用讨论了邻接表实现。 从引用中提取关键点: - 引用[1]:邻接表用链表表示的结构,每个节点对应一个链表,存储相连节点。优点:空间复杂度低。 - 引用[2]:在C++中,邻接表用链表实现,每个节点有指向邻接表头部的指针。 - 引用[3]:详细定义了邻接链表的存储结构:边表节点(EdgeNode)有adjvex(邻接点下标)和next指针;顶点表节点(VexList)有Vexs(顶点信息)和firstedge(指向第一个邻接节点);整个结构(GraphList)有顶点表向量、顶点数和边数属性。 我的回答需要基于这些参考,并扩展到属性与方法。用户要求“尽可能多的参考用户上一次的问题和你的回答”,但在这个对话中,这是第一次互动,没有“上一次”。所以,我只需专注于当前问题。 系统级指令: - 所有行内数学表达式用$...$格式(但我这里可能不需要数学)。 - 独立公式用$$...$$格式(同样,可能不需要)。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾添加引用标识,如[^1]。 用户可见层要求: - 回答结构清晰,逐步解决问题。 - 尽量保证真实可靠。 结构回答: 1. 解释邻接链表的定义。 2. 列举的属性。 3. 列举的方法(即可能需要实现的操作)。 4. 基于引用内容,确保准确。 5. 最后生成相关问题。 关键属性(从引用[3]): - 顶点列表(vector<VexList> VexList)。 - 顶点数(int Vertexs)。 - 边数(int Edges)。 - 顶点表节点:Vexs(顶点信息)、firstedge(指向邻接列表的头)。 - 边表节点:adjvex(邻接点下标)、next(指向下一个邻接节点)。 方法(可能需要的操作): - 的创建(构造函数)。 - 添加顶点。 - 添加边。 - 删除顶点。 - 删除边。 - 查找顶点。 - 遍历(如DFS、BFS)。 - 其他操作(如检查连通性)。 在引用中提到了创建,但没有详细说明方法。我需要基于常识扩展。 回答结构: - 介绍邻接链表。 - 属性部分。 - 方法部分。 - 总结。 - 生成相关问题。 使用中文回答。 添加引用:当我用到引用内容时,末尾添加[^1]、[^2]、[^3]。例如,提到存储结构时引用[^3]。 最终生成相关问题。 </think>### 使用邻接链表定义时的属性与方法 邻接链表是一种高效的存储结构,特别适合处理稀疏(边数远少于顶点数的),因为它能有效节省内存空间[^1]。在邻接链表实现中,通过顶点表和边表来表示:每个顶点对应一个链表(边表),存储其所有相邻顶点。下面我将基于参考引用,详细介绍使用邻接链表定义时需要定义的属性与方法。这些内容基于标准数据结构的实现,确保可靠性和实用性。 #### 1. **的属性(数据结构成员)** 在邻接链表定义中,的属性包括顶点信息、边信息以及链表结构。核心属性如下: - **顶点表(VexList)**:一个集合(如数组或向量),存储所有顶点信息。每个顶点项包括: - `Vexs`:存储顶点数据(如顶点名称或ID),类型通常是字符串或整数[^3]。 - `firstedge`:一个指向边表头节点的指针,表示该顶点的第一个邻接点[^3]。 - **边表节点(EdgeNode)**:用于构建邻接链表,每个节点表示一条边。包括: - `adjvex`:存储邻接顶点的下标(在顶点表中的索引),类型为整数[^3]。 - `next`:一个指向下一个边表节点的指针,用于链表串联[^3]。 - **的概要属性**: - `Vertexs`:的顶点总数,类型为整数,便于快速查询的大小[^3]。 - `Edges`:的边总数,类型为整数,用于统计和优化操作[^3]。 - `VexList`:一个动态容器(如C++的`vector`),存储所有顶点表的项,类型为`vector<VexList>`[^3]。 这些属性确保了的存储空间复杂度为$O(|V| + |E|)$(其中$|V|$是顶点数,$|E|$是边数),比邻接矩阵的$O(|V|^2)$更高效,尤其适合稀疏[^1][^3]。 #### 2. **的方法(操作函数)** 在定义邻接链表时,需要实现一系列方法来操作的创建、修改和查询。这些方法通常作为类的成员函数在代码中实现。核心方法包括: - **的初始化与创建**: - `构造函数`:初始化,设置顶点数`Vertexs`和边数`Edges`为0,并分配顶点表内存[^3]。 - `初始化顶点表`:根据输入添加顶点数据到`VexList`,并设置每个`firstedge`为`nullptr`(空指针)[^2][^3]。 - **顶点操作**: - `添加顶点(vertex)`:在顶点表`VexList`中添加一个新顶点,设置`Vexs`为新顶点数据,并初始化`firstedge`为`nullptr`。同时更新`Vertexs`属性[^3]。 - `删除顶点(vertex)`:移除指定顶点及其所有相关边。这需要遍历边表,删除指向该顶点的边,并更新邻接链表。同时减少`Vertexs`和`Edges`计数[^1][^3]。 - `查找顶点(vertex)`:遍历顶点表`VexList`,返回顶点的索引或是否存在的信息,时间复杂度为$O(|V|)$[^3]。 - **边操作**: - `添加边(src, dest)`:在源顶点`src`的边链表中插入一个新边表节点(`EdgeNode`),其中`adjvex`设置为目标顶点`dest`的索引。使用`next`指针维护链表顺序,并递增`Edges`属性。例如,有向图只需添加一条边;无向需同时在`dest`的链表中添加反向边[^2][^3]。 - `删除边(src, dest)`:从源顶点`src`的链表中移除指向`dest`的边表节点,并更新链表。同时减少`Edges`属性[^1]。 - `检查边存在(src, dest)`:遍历`src`的边链表,检查`adjvex`是否匹配`dest`,返回布尔值[^3]。 - **遍历与查询**: - `广度优先遍历(BFS)`:从起始顶点出发,使用队列遍历所有可达顶点,输出顶点序列。 - `深度优先遍历(DFS)`:使用递归或栈实现深度搜索,常用于路径查找或连通性分析。 - `获取邻接点(vertex)`:返回指定顶点的所有邻接顶点列表,通过遍历其`firstedge`链表实现[^2][^3]。 - `统计信息`:如返回顶点数`Vertexs`或边数`Edges`,时间复杂度$O(1)$[^3]。 这些方法确保了操作的完整性:添加顶点和边的时间复杂度为$O(1)$或$O(deg(v))$(取决于实现),而遍历操作如BFS/DFS的时间复杂度为$O(|V| + |E|)$[^1][^3]。在实际代码中(如C++),这些属性与方法通常封装在一个类中,例如: ```cpp class Graph { private: struct EdgeNode { int adjvex; EdgeNode* next; EdgeNode(int adj) : adjvex(adj), next(nullptr) {} }; struct VexList { string Vexs; // 顶点数据 EdgeNode* firstedge; // 指向第一个邻接点 VexList() : firstedge(nullptr) {} }; vector<VexList> VexList; // 顶点表 int Vertexs, Edges; // 顶点数和边数 public: Graph() : Vertexs(0), Edges(0) {} // 构造函数 void addVertex(string vertex); // 添加顶点方法 void addEdge(int src, int dest); // 添加边方法 // 其他方法... }; ``` #### 总结 使用邻接链表定义时,属性主要包括顶点表、边表节点、顶点数和边数;方法涵盖的初始化、顶点/边的增删改查、以及遍历算法。这种结构在空间效率上优于邻接矩阵,特别适合节点数大但边稀疏的场景[^1][^3]。实现时需注意指针管理和内存泄漏问题,确保可靠性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值