算法与数据结构--图论基础知识

1、图论基础概念 Graph Theory

:是由由 节点 和 边 组成的数据模型,它有两个重要部分

  • 1、节点
  • 2、边

节点是两个村, 边表示两个村直接连通的道路
或者节点是人, 边表示人与人之间的关系。
点是一个域名, 边是域名之间的调整

无向图:边是没有方向的(如两个村是否有道路连接)
有向图:边有方向(人际关系网,你认识他,他不认识你)

有向图会使图更加复杂。具有不对称性。

可以把无向图认为是一种特殊有向图,是双向的。

无权图:每条边是只表示一种状态的,没有值,如人际关系网中,如果两个人之间是否连接只是表示认识与不认识,则无权图即可。如下示例:

有权图:如果两个人之间的连线除了表示 认识与不认识的状态,还要表示认识的程度,就可以加上一个数值表示, 5分熟还是9分熟。如下示例:

自环边:两个城市之间,不止一条了,可能有三条

平行边:一个城市自己向外扩散的路。

平行边和自环边不会改变 点与点的连通性,通常在较复杂图中才会涉及。

图的连通性: 图中的各个节点是否连通。

邻接矩阵:一个矩阵, 适合表示稠密图(Dense Graph),矩阵中每个位置标示两个点的连接状态。

邻接表:每一行是一个链表, 适合表示 稀疏图(Sparse Graph)。和节点0相连的只有节点1, 所以节点0的链表就只有 节点1一个点, 而节点1 和 0、2、3 三个节点相连,则这个链表存有三个节点。

稠密图:在所有节点中, 每个节点都和其中很多的其他节点连接,即每个点的边的数量 不会比节点数量少很多。

稀疏图(Sparse Graph):在所有节点中, 每个节点都和其中较少的其他节点连接,每个点的边的数量 远少于节点数量。

完全图:每个节点都和其他所有节点相连。

2 图的基本实现

邻接矩阵的实现(稠密图):

/**
 * 用邻接矩阵来表示 无向图(用于稠密图)
 * @author admin
 *
 */
public class DenseGraph {
	
	private int n;//存放图的节点数量
	private int m;//存放图的边数量
	private boolean directed;//是有向图还是无向图
	private int[][] g;//创建矩阵
	

	public DenseGraph(int n, boolean directed) {
		this.n = n;
		this.m = 0;
		this.directed = directed;
		g = new int[n][n];//先创建一个n阶矩阵,由于是int型, 所有值默认为0
	}
	
	
	/**
	 * 返回有多少节点
	 */
	public int getV() {
		return n;
	}
	
	/**
	 * 返回有多少条边
	 */
	public int getE() {
		return m;
	}
	
	/**
	 * 连接两个节点,添加两个节点的边
	 * @param v
	 * @param w
	 */
	public void addEdge(int v, int w) {
		g[v][w] = 1;//两点连接, 用1表示
		if(!directed) {
			g[w][v] = 1;//如果是无向表,则两个方向的值都是相等的
		}
		if(!hasEdge(v, w)) {
			m++; //边增加一条
		}
	}
	
	/**
	 * 判断两个点是否有边
	 */
	private boolean hasEdge(int v, int w) {
		return g[v][w] == 1;
	}
}

邻接表的实现(稠密图):

/**
 * 用邻接表 来表示 无向图(用于稀疏图)
 * @author admin
 *
 */
public class SparseGraph {
	
	private int n;//存放图的节点数量
	private int m;//存放图的边数量
	private boolean directed;//是有向图还是无向图
	ArrayList[] g;//邻接表
	

	public SparseGraph(int n, boolean directed) {
		this.n = n;
		this.m = 0;
		this.directed = directed;
		g = new ArrayList[n];//创建n行的邻接表
		for(int i=0; i<n; i++) {
			g[i] = new ArrayList<>();
		}
	}
	
	
	/**
	 * 返回有多少节点
	 */
	public int getV() {
		return n;
	}
	
	/**
	 * 返回有多少条边
	 */
	public int getE() {
		return m;
	}
	
	/**
	 * 连接两个节点,添加两个节点的边
	 * @param v
	 * @param w
	 */
	public void addEdge(int v, int w) {
		g[v].add(w);//v和m相连, 则将m添加到v的链表中。
		if(v!= w && !directed) { //本例中避免自环边的添加
			g[w].add(v);
		}
		if(!hasEdge(v, w)) {
			m++; //边增加一条
		}
	}
	
	/**
	 * 判断两个点是否有边,避免平行边
	 */
	private boolean hasEdge(int v, int w) {
		return g[v].contains(w);
	}
}

3 图的常见操作----遍历邻边

按照上述实现的代码,在邻接矩阵中只需要遍历每个数组中哪些位置是1即可得到 与之直接相连的点。而邻接表中, 只需要 遍历每一行的链表即可得到直接连接的点, 很好实现。

但是,为了不对外暴露我们图的内部的具体实现,我们可以用一种 很常见很好用的设计模式– "迭代器模式, 来完全屏蔽我们内部对数据的实现,也不用对外直接暴露数据,防止数据被任意篡改,甚至能将接口往上进行抽象, 让不同图对外暴数据的接口相同,从而更好的抽象类。

通过迭代器,我们可以遍历整个图中的数据,去得到想查找的点 有哪些邻边。

在稠密图的实现中,我们增加一个内部类 作为迭代器:

static class GraphIterator {
	private DenseGraph graph;//要遍历的图
	private int n;//要遍历的节点
	private int index;//脚标
	public GraphIterator(DenseGraph graph, int n) {
		this.graph = graph;
		this.n = n;
	}
	
	public int begin() {
		index = -1;
		return next();
	}
	
	/**
	 * 返回相连节点的编号
	 * @return
	 */
	public int next() {
		for(index += 1; index < graph.g[n].length; index++ ) {
			if(graph.g[n][index] == 1) {
				return index;
			}
		}
		return -1;
	}
	
	public boolean isEnd() {
		return index >= graph.g[n].length;
	}
}

同理的,我们在稀疏图的实现中,增加一个内部类:

static class GraphIterator {
	private SparseGraph graph;//要遍历的图
	private int n;//要遍历的节点
	private int index;//脚标
	public GraphIterator(SparseGraph graph, int n) {
		this.graph = graph;
		this.n = n;
	}
	
	public int begin() {
		index = -1;
		return next();
	}
	
	/**
	 * 返回相连节点的编号
	 * @return
	 */
	public int next() {
		for(index += 1; index < graph.g[n].size(); index++ ) {
			if((int)graph.g[n].get(index) == 1) {
				return index;
			}
		}
		return -1;
	}
	
	public boolean isEnd() {
		return index >= graph.g[n].size();
	}
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值