邻接链表法
基本思想:对图的每个顶点建立一个单链表,存储该顶点所有邻接顶点及其相关信息。每一个单链表设一个表头结点。
第i个单链表表示依附于顶点Vi的边(对有向图是以顶点Vi为头或尾的弧)。
1结点结构与邻接链表示例
链表中的结点称为表结点,每个结点由三个域组成,如图(a)所示。其中邻接点域(adjvex)指示与顶点Vi邻接的顶点在图中的位置(顶点编号),链域(nextarc)指向下一个与顶点Vi邻接的表结点,数据域(info)存储和边或弧相关的信息,如权值等。对于无权图,如果没有与边相关的其他信息,可省略此域。
每个链表设一个表头结点(称为顶点结点),由两个域组成,如图(b)所示。链域(firstarc)指向链表中的第一个结点,数据域(data)存储顶点名或其他信息。
在图的邻接链表表示中,所有顶点结点用一个向量以顺序结构形式存储,可以随机访问任意顶点的链表,该向量称为表头向量,向量的下标指示顶点的序号。
用邻接链表存储图时,对无向图,其邻接链表是唯一的,如图7-10所示;对有向图,其邻接链表有两种形式,如图7-11所示。
2邻接表法的特点
◆表头向量中每个分量就是一个单链表的头结点,分量个数就是图中的顶点数目;
◆在边或弧稀疏的条件下,用邻接表表示比用邻接矩阵表示节省存储空间;
◆在无向图,顶点Vi的度是第i个链表的结点数;
◆对有向图可以建立正邻接表或逆邻接表。正邻接表是以顶点Vi为出度(即为弧的起点)而建立的邻接表;逆邻接表是以顶点Vi为入度(即为弧的终点)而建立的邻接表;
◆在有向图中,第i个链表中的结点数是顶点Vi的出(或入)度;求入(或出)度,须遍历整个邻接表;
◆在邻接表上容易找出任一顶点的第一个邻接点和下一个邻接点;
3结点及其类型定义
顶点Vererex.java
package datastructure.graph;
import datastructure.list.LinkList;
import datastructure.list.List;
/**
* 图的顶点
* @author luoweifu
*
*/
public class Vertex{
private Object data;
private ArcEdge firstArc;
/**
* 构造函数
*/
public Vertex() {
data = null;
firstArc = null;
}
/**
* 构造函数
* @param data 顶点的数据
*/
public Vertex(Object data) {
this.data = data;
this.firstArc = null;
}
/**
* 获得顶点的数据
* @return 顶点的数据
*/
public Object getData() {
return data;
}
/**
* 设置顶点的数据
* @param data 顶点的数据
*/
public void setData(Object data) {
this.data = data;
}
/**
* 获得第一个孤结点
* @return
*/
public ArcEdge getFirstArc() {
return firstArc;
}
/**
* 设置第一个孤结点
* @param firstArc
*/
public void setFirstArc(ArcEdge firstArc) {
this.firstArc = firstArc;
}
@Override
public boolean equals(Object obj) {
Vertex v = (Vertex)obj;
if(data.equals(v.getData()) )//&& firstArc.equals(v.getFirstArc())
return true;
return false;
}
@Override
public String toString() {
return data + " " + firstArc;
}
}
孤结点ArcEdge.java
package datastructure.graph;
/**
* 弧结点定义
* @author luoweifu
*
*/
public class ArcEdge extends Edge{
private Vertex vertex;
private ArcEdge prior;
private ArcEdge next;
/**
* 构造函数
*/
public ArcEdge() {
super();
}
/**
* 构造函数
* @param weight 权值
*/
public ArcEdge(double weight) {
super(weight);
prior = null;
next = null;
}
/**
* 构造函数
* @param info 边的信息
* @param weight 权值
*/
public ArcEdge(Object info, double weight) {
super(info, weight);
prior = null;
next = null;
}
/**
* 构造函数
* @param info 边的信息
* @param weight 权值
* @param vertex 顶点
*/
public ArcEdge(Object info, double weight, Vertex vertex) {
this(info, weight);
this.vertex = vertex;
prior = null;
next = null;
}
/**
* 获得顶点数据
* @return
*/
public Vertex getVertex() {
return vertex;
}
/**
* 设置顶点
* @param vertex
*/
public void setVertex(Vertex vertex) {
this.vertex = vertex;
}
/**
* 获得上一个孤结点
* @return
*/
public ArcEdge getPrior() {
return prior;
}
/**
* 设置上一个孤结点
* @param prior
*/
public void setPrior(ArcEdge prior) {
this.prior = prior;
}
/**
* 获得下一个孤结点
* @return
*/
public ArcEdge getNext() {
return next;
}
/**
* 设置下一个孤结点
* @param next
*/
public void setNext(ArcEdge next) {
this.next = next;
}
@Override
public int compareTo(Object o) {
double w2 = ((Edge)o).getWeight();
if(this.weight< w2)
return -1;
else if(this.weight > w2)
return 1;
return 0;
}
@Override
public boolean equals(Object obj) {
ArcEdge arc = (ArcEdge)obj;
if(this.next == arc.next && this.weight == arc.weight)
return true;
return false;
}
@Override
public Object getFirstVertex() {
return prior.vertex.getData();
}
@Override
public Object getSecondVertex() {
return vertex.getData();
}
}
邻接链表法表示的图ListGraph.java
package datastructure.graph;
import datastructure.list.ArrayList;
import datastructure.list.List;
import datastructure.queue.ArrayQueue;
import datastructure.queue.Queue;
public class ListGraph implements Graph {
private List<Vertex> vertexs;
private int edgeNum; //边的条数
private enum Visit{unvisited, visited};
public ListGraph() {
vertexs = new ArrayList();
}
public List getVertexs() {
return vertexs;
}
public ListGraph(Object[] vexs) {
this();
for(int i=0; i<vexs.length; i++) {
vertexs.add(new Vertex(vexs[i]));
}
}
private Vertex find(Object v) {
Vertex vex = new Vertex(v);
int i = vertexs.indexOf(vex);
if(i<0) {
return null;
//throw new ArrayIndexOutOfBoundsException("顶点" + v + "不存在!");
}
return (Vertex)vertexs.get(i);
}
@Override
public void addEdge(Object v1, Object v2, double weight) {
Vertex vex1 = find(v1);
Vertex vex2 = find(v2);
if(vex1 != null && vex2 != null) {
ArcEdge e = new ArcEdge(null, weight, vex2);
if(vex1.getFirstArc() == null) {
vex1.setFirstArc(e);
} else {
ArcEdge arc = vex1.getFirstArc();
while(arc.getNext() != null) {
arc = arc.getNext();
}
arc.setNext(e);
e.setPrior(arc);
}
edgeNum ++;
} else {
throw new ArrayIndexOutOfBoundsException("顶点" + v1 + "或" + v2 + "不存在!");
}
}
@Override
public void addEdge(Object v1, Object v2, Object info, double weight) {
Vertex vex1 = find(v1);
Vertex vex2 = find(v2);
if(vex1 != null && vex2 != null) {
ArcEdge e = new ArcEdge(info, weight, vex2);
if(vex1.getFirstArc() == null) {
vex1.setFirstArc(e);
} else {
ArcEdge arc = vex1.getFirstArc();
while(arc.getNext() != null) {
arc = arc.getNext();
}
arc.setNext(e);
e.setPrior(arc);
}
edgeNum ++;
} else {
throw new ArrayIndexOutOfBoundsException("顶点" + v1 + "或" + v2 + "不存在!");
}
}
@Override
public void addVex(Object v) {
vertexs.add(new Vertex(v));
}
@Override
public String bfs(Object o) {
// ----------------该方法还有点问题-------------
Visit visit[] = new Visit[vertexs.size()];
for(int i=0; i<vertexs.size(); i++)
visit[i] = Visit.unvisited;
StringBuilder sb = new StringBuilder();
Vertex vex = new Vertex(o);//find(o);
bfs(vex, visit, sb);
return sb.toString();
}
private void bfs(Vertex vex, Visit[] visit, StringBuilder sb) {
Queue queue = new ArrayQueue();
int n = vertexs.indexOf(vex);
sb.append(vex.getData() + "\t");
visit[n] = Visit.visited;
queue.push(vex);
while(!queue.isEmpty()) {
Vertex u = (Vertex) queue.deQueue();
Vertex v = getFirstVertex(u);
while(null != v) {
if(Visit.unvisited == visit[vertexs.indexOf(v)]) {
sb.append(v.getData() + "\t");
visit[vertexs.indexOf(v)] = Visit.visited;
queue.push(v);
}
v = getNextVertex(u, v);
}
}
}
@Override
public String dfs(Object o) {
// ----------------该方法还有点问题-------------
Visit visit[] = new Visit[vertexs.size()];
for(int i=0; i<vertexs.size(); i++)
visit[i] = Visit.unvisited;
StringBuilder sb = new StringBuilder();
Vertex vex = new Vertex(o);//find(o);
dfs(vex, visit, sb);
return sb.toString();
}
private void dfs(Vertex vex, Visit[] visit, StringBuilder sb) {
int n = vertexs.indexOf(vex);
sb.append(vex.getData() + "\t");
visit[n] = Visit.visited;
Vertex v = getFirstVertex(vex);
while(null != v) {
if(Visit.unvisited == visit[vertexs.indexOf(v)])
dfs(v, visit, sb);
v = getNextVertex(vex, v);
}
}
@Override
public void clear() {
vertexs.clear();
}
@Override
public int getEdgeSize() {
return edgeNum;
}
@Override
public Object getFirstVertex(Object v) {
Vertex vex = find(v);
return getFirstVertex(vex).getData();
}
private Vertex getFirstVertex(Vertex v) {
if(v.getFirstArc() != null && v.getFirstArc().getVertex() != null)
return v.getFirstArc().getVertex();
return null;
}
@Override
public Object getNextVertex(Object v1, Object v2) {
// ----------------该方法还有点问题-------------
Vertex vex1 = find(v1);
Vertex vex2 = find(v2);
System.out.println("v1:" + v1);
System.out.println("v2:" + v2);
System.out.println("vex1:" + vex1);
System.out.println("vex2:" + vex2);
return getNextVertex(vex1, vex2);
}
/**
* ----------------该方法还有点问题-------------
* @param vex1
* @param vex2
* @return
*/
private Vertex getNextVertex(Vertex vex1, Vertex vex2) {
ArcEdge arc = vex1.getFirstArc();
while(arc.getNext() != null && arc.getVertex()!=vex2) {
arc = arc.getNext();
}
if(arc.getVertex() != null) {
//System.out.println(arc.getVertex());
return arc.getNext().getVertex();
}
return null;
}
@Override
public int getVertexSize() {
return vertexs.size();
}
@Override
public void removeEdge(Object v1, Object v2) {
Vertex vex1 = find(v1);
Vertex vex2 = find(v2);
if(vex1 != null && vex2 != null) {
ArcEdge arc = vex1.getFirstArc();
while(arc.getNext() != null && arc.getVertex() != vex2) {
arc = arc.getNext();
}
if(arc.getVertex() == vex2) {
ArcEdge priEdge = arc.getPrior();
ArcEdge nextEdge = arc.getNext();
priEdge.setNext(nextEdge);
nextEdge.setPrior(priEdge);
edgeNum --;
} else {
throw new ArrayIndexOutOfBoundsException("边" + v1 + "到" + v2 + "不存在!");
}
} else {
throw new ArrayIndexOutOfBoundsException("顶点" + v1 + "或" + v2 + "不存在!");
}
}
@Override
public void removeVex(Object v) {
for(int i=0; i<vertexs.size(); i++) {
Vertex vex1 = (Vertex)(vertexs.get(i));
ArcEdge arc = vex1.getFirstArc();
if(arc != null) {
while(arc.getNext() != null) {
if(arc.getVertex().getData() == v) {
removeEdge(vex1, v);
}
}
}
}
Vertex vex = find(v);
if(vex != null) {
int i = vertexs.indexOf(vex);
vertexs.remove(i);
}
}
@Override
public String printGraph() {
StringBuilder sb = new StringBuilder();
for(int i=0; i<vertexs.size(); i++) {
Vertex vex = (Vertex) vertexs.get(i);
sb.append("\n顶点:" + vex.getData() + "\t");
ArcEdge arc = vex.getFirstArc();
if(arc != null) {
sb.append("孤," + arc.getVertex().getData());
while(arc.getNext() != null) {
sb.append("\t" + arc.getNext().getVertex().getData());
arc = arc.getNext();
}
}
}
return sb.toString();
}
}
分享到:
相关推荐
5. **高级数据结构**:如树(二叉链表)、图(邻接表)等,都以链表为基础。 ### 五、链表与数组的比较 1. 存储效率:数组存储空间连续,访问速度快;链表需要额外的指针存储,空间利用率较低。 2. 插入与删除:...
本文将深入探讨两种常用的图存储结构:邻接表和邻接矩阵,并介绍如何在这两种结构上进行节点和边的插入与删除操作。 ### 邻接表 邻接表是一种节省空间的图存储方式,特别适合于稀疏图(边的数量远小于节点数量的...
本作业主要探讨如何将NFA转换为DFA,并通过数据结构——邻接表来实现这一过程。 非确定性有限自动机(NFA)是一种允许存在多种转移路径的自动机,它在读取输入符号时可以有多个可能的状态转移。NFA通常用五元组 (Q,...
在本实验中,我们将深入探讨数据结构中的一个重要概念——邻接表,这是图数据结构的一种高效表示方法。湖南大学的数据结构课程通过实验5来让学生掌握邻接表的实现与应用。下面,我们将详细讨论邻接表及其相关知识,...
本主题将深入探讨“图”的一种高效表示方法——邻接表。 邻接表是图数据结构的一种高效实现,特别适用于稀疏图(边的数量远小于顶点数量的平方)。在邻接表中,每个顶点都有一个链表或数组,存储与之相连的所有顶点...
本主题将深入探讨两种常见的图存储方式——邻接矩阵和邻接表,以及如何在这两种存储方式下实现深度优先遍历(DFS)和广度优先遍历(BFS)。 首先,邻接矩阵是一种直观的图表示方法,它使用二维数组来存储图中每个...
这里,我们采用两种不同的图存储结构——邻接矩阵和邻接表,并结合MFC(Microsoft Foundation Classes)框架来实现。下面将详细讨论这些知识点。 首先,无向图是一种图论中的基本概念,它表示节点之间的相互连接...
本文将深入探讨一种用于表示图的数据结构——邻接表。邻接表是一种高效且节省空间的图表示方法,尤其适用于稀疏图(边的数量远小于顶点数量的平方)。 首先,我们需要了解图的基本概念。图是由顶点(或节点)和边...
本主题主要涵盖了三种关键概念:邻接矩阵、邻接表以及图的两种遍历方法——深度优先搜索(DFS)和广度优先搜索(BFS)。 首先,邻接矩阵是一种表示图的数据结构,它是一个二维数组,其中的每个元素代表图中对应节点...
通过以上分析可以看出,这段代码不仅实现了图的基本存储结构——邻接表,还提供了两种重要的图遍历算法:深度优先搜索和广度优先搜索,以及一个队列辅助结构。这些基础知识对于理解和实现图的相关算法非常有帮助。
在实验"数据结构实验---图的储存与遍历.doc"中,主要探讨了图的两种常见存储方法——邻接矩阵和邻接表,以及如何在这两种存储方式下实现深度优先遍历(DFS)和广度优先遍历(BFS)。 邻接矩阵是表示图的一种直观方法,...
在这个哈密顿回路的回溯法实现中,首先需要定义图的数据结构,通常可以使用邻接矩阵或邻接表来表示。然后,定义一个函数用于递归地探索所有可能的路径。该函数会从一个未访问过的顶点开始,标记它为已访问,然后尝试...
`LGraph`由邻接链表数组和顶点、边数量构成,每个顶点(`VNode`)有一个链表(`Arode`),链表中的每个元素记录了相邻顶点的位置和指向下一个弧的指针。 程序主要包含六个函数: 1. **主函数 `main()`**:初始化图...
本篇文档涉及的是数据结构中的一个重要概念——无向图的邻接表建立,这在图形算法和网络模型等领域有着广泛的应用。 无向图是一种图的类型,其中任意两个顶点之间的边没有方向性,即如果有一条边连接A和B,那么也...
数据结构实验报告主要探讨了两种不同的图存储方法——邻接矩阵和邻接表,并通过实践加深了对这两种方法的理解。实验旨在通过实现深度优先遍历(DFS)算法,来理解和应用这些数据结构。 1. 邻接矩阵是图的一种直观...
在本篇文章中,我们将深入探讨一种特定的图算法——关键路径法(Critical Path Method, CPM),并了解如何使用邻接表和栈来实现这一算法。 #### 邻接表与栈的运用 邻接表是一种存储图的高效方式,它对于无向图或有...
本文将详细介绍两种常见的图的搜索算法——深度优先搜索(DFS)和广度优先搜索(BFS),以及与之相关的数据结构:边链表和关系矩阵。 **深度优先搜索(DFS)**是一种用于遍历或搜索树或图的算法。DFS策略是尽可能深...
以上就是关于“数据结构-图的相关代码”所涉及的知识点,包括图的两种常见表示法——数组表示法和邻接表表示法,以及有向图的邻接矩阵表示法和图的两种遍历方法。这些概念在解决实际问题,如网络路由、社交网络分析...
总之,本实验报告不仅让我们掌握了图的两种基本表示方法——邻接矩阵和邻接表的原理与实现,而且加深了对图这种数据结构的理解,为进一步探索图论及其实用问题提供了有力的工具和方法。通过对比分析实验结果,我们...