图论

图论

图论〔Graph Theory〕是数学的一个分支。它以图为研究对象。图论中的图是由若干给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系,用点代表事物,用连接两点的线表示相应两个事物间具有这种关系

1.1图的重要组成部分

节点:Vertex
边:Edge
可以表示:交通运输、社交网络、互联网、工作安排、脑区活动、程序状态执行

在这里插入图片描述

1.2图的分类
  • 无向图
    在这里插入图片描述
  • 有向图
    在这里插入图片描述
  • 无权图
    在这里插入图片描述
  • 有权图
    在这里插入图片描述
1.2图的表示

链接矩阵,用矩阵的方式表示图

  • 无向图的表示
    在这里插入图片描述
  • 有向图的表示

在这里插入图片描述

领接表,类似哈希表

  • 无向图的表示
    在这里插入图片描述
  • 有向图的表示
    在这里插入图片描述
    Graph接口的定义
public interface Graph {
	//获取节点个数
	public int V();
	//获取边的个数
	public int E();
	//向图中添加一条边v-w 或 v->w
	public void addEdge(int v,int w);
	//判断图中两个节点之间是否有边(非连通,是直连)
	public boolean hasEdge(int v,int w);
	//打印图的内容
	public void show();
	//返回一个图中一个节点所有的邻边
	public Iterable<Integer> adj(int v);
}

DenseGraph类的定义(邻接矩阵-稠密图)

import java.util.Vector;

public class DenseGraph implements Graph{
	
	private boolean[][] g;//图的具体数据
	
	private int n;//节点的个数
	private int m;//边的个数
	
	private boolean directed;//是否为有向图  true为有向图,false为无向图
	public DenseGraph(int n,boolean directed) {
		if(n<0){
			throw new IllegalArgumentException("图中的节点个数必须大于等于0");
		}
		this.n=n;
		this.directed=directed;
		this.m=0;
		this.g=new boolean[n][n];	
	}
	public DenseGraph() {
		this(0,false);
	}
	@Override
	public int V() {
		return this.n;
	}

	@Override
	public int E() {
		return this.m;
	}

	@Override
	public void addEdge(int v, int w) {
		if(v<0||v>=n){
			throw new IllegalArgumentException(v+"节点不存在");
		}
		if(w<0||w>=n){
			throw new IllegalArgumentException(w+"节点不存在");
		}
		//不考虑平行边和自环边
		//考虑有向和无向
		if(v==w){	//防止自环边
			return;
		}
		if(hasEdge(v,w)){//如果已存在边,则不加入
			return;
		}
		g[v][w]=true;//有向图,单向
		if(!directed){
			g[w][v]=true;//无向图,双向
		}
		m++;
	}

	@Override
	public boolean hasEdge(int v, int w) {
		if(v<0||v>=n){
			throw new IllegalArgumentException(v+"节点不存在");
		}
		if(w<0||w>=n){
			throw new IllegalArgumentException(w+"节点不存在");
		}
		return g[v][w];
	}

	@Override
	public void show() {
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
				System.out.print((g[i][j]==true? 1:0)+" ");
			}
			System.out.println();
		}
	}

	@Override
	public Iterable<Integer> adj(int v) {
		if(v<0||v>=n){
			throw new IllegalArgumentException(v+"节点不存在");
		}
		Vector<Integer> adjv=new Vector<Integer>();
		for(int i=0;i<n;i++){
			if(g[v][i]){
				adjv.add(i);
			}
		}
		return adjv;
	}

}

SparesGraph类的定义(邻接表–稀疏图)

import java.util.Vector;

//邻接表--稀疏图
public class SparesGraph implements Graph{
	private int n;
	private int m;
	private boolean directed;
	private Vector<Integer>[] g;
	public SparesGraph(int n,boolean directed) {
		if(n<0){
			throw new IllegalArgumentException("节点个数必须大于等于0");
		}
		this.n=n;
		this.m=0;
		this.directed=directed;
		this.g=(Vector<Integer>[])new Vector[n];
		for(int i=0;i<n;i++){
			g[i]=new Vector<Integer>();
		}
	}
	@Override
	public int V() {
		return this.n;
	}
	@Override
	public int E() {
		return this.m;
	}
	@Override
	public void addEdge(int v, int w) {
		if(v<0||v>=n){
			throw new IllegalArgumentException(v+"节点不存在");
		}
		if(w<0||w>=n){
			throw new IllegalArgumentException(w+"节点不存在");
		}	
		if(v==w){	//防止自环边
			return;
		}
		if(hasEdge(v, w)){
			return;
		}
		g[v].add(w);
		if(!directed){
			g[w].add(v);
		}
		m++;
	}
	@Override
	public boolean hasEdge(int v, int w) {
		if(v<0||v>=n){
			throw new IllegalArgumentException(v+"节点不存在");
		}
		if(w<0||w>=n){
			throw new IllegalArgumentException(w+"节点不存在");
		}
		return g[v].contains(w);
	}
	@Override
	public void show() {
		for(int i=0;i<n;i++){
			System.out.print("vertext"+i+":");
			for(int j=0;j<g[i].size();j++){
				System.out.print(g[i].get(j)+" ");
			}
			System.out.println();
		}
	}
	@Override
	public Iterable<Integer> adj(int v) {
		return g[v];
	}
}

深度优先遍历

深度优先搜索,是图论中的经典算法。其利用深度优先搜索算法可以产生目标图的相应拓扑排序表,利用拓扑排序表可以方便的解决很多相关的图论问题,如最大路径问题等等。

  • 过程

设x是当前被访问顶点,在对x做过访问标记后,选择一条从x出发的未检测过的边(x,y)。若发现顶点y已访问过,则重新选择另一条从x出发的未检测过的边,否则沿边(x,y)到达未曾访问过的y,对y访问并将其标记为已访问过;然后从y开始搜索,直到搜索完从y出发的所有路径,即访问完所有从y出发可达的顶点之后,才回溯到顶点x,并且再选择一条从x出发的未检测过的边。上述过程直至从x出发的所有边都已检测过为止。此时,若x不是源点,则回溯到在x之前被访问过的顶点;否则图中所有和源点有路径相通的顶点(即从源点可达的所有顶点)都已被访问过,若图G是连通图,则遍历过程结束,否则继续选择一个尚未被访问的顶点作为新的顶点,继续遍历。

  • 源码
//深度优先遍历
public class Components {
	private Graph G;
	private boolean[] visited;
	private int ccount;
	private int[] id;
	private StringBuffer path;
	public Components(Graph graph) {
		path=new StringBuffer();
		this.G=graph;
		visited=new boolean[G.V()];//false
		id=new int[G.V()];//0
		ccount=0;
		for (int i = 0; i < G.V(); i++) {
			visited[i]=false;
			id[i]=-1;
		}
		for (int i = 0; i < G.V(); i++) {
			if(!visited[i]){
				dfs(i);
				path.append("\n");
				ccount++;
			}
		}
	}
	private void dfs(int v) {
		visited[v]=true;	//标记已经被访问
		id[v]=ccount;
		path.append(v+"->");
		for (int i : G.adj(v)) {
			if(!visited[i]){
				dfs(i);
			}
		}
	}
	public int ccount(){
		return ccount;
	}
	public boolean isConnected(int v,int w){
		if(v<0||v>=G.V()){
			throw new IllegalArgumentException(v+"节点不存在");
		}
		if(w<0||w>=G.V()){
			throw new IllegalArgumentException(w+"节点不存在");
		}
		return id[v]==id[w];
	}
	public String getDFSPath(){
		return path.toString();
	}
}

广度优先遍历(无权图的最短路径)

广度优先遍历是连通图的一种遍历策略。因为它的思想是从一个顶点V0开始,辐射状地优先遍历其周围较广的区域,故得名。

  • 过程

1、从图中某个顶点V0出发,并访问此顶点;
2、从V0出发,访问V0的各个未曾访问的邻接点W1,W2,…,Wk;然后,依次从W1,W2,…,Wk出发访问各自未被访问的邻接点;
3、重复步骤2,直到全部顶点都被访问为止。

  • 源码
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
import java.util.Vector;

//无权图的最短路径--广度优先遍历
public class ShortestPath {
	private Graph G;
	private int s;
	private boolean[] visited;
	private int[] from;
	private int[] ord;
	public ShortestPath(Graph graph,int s) {
		this.G=graph;
		if(s<0||s>G.V()){
			throw new IllegalArgumentException("节点不存在");
		}
		
		visited=new boolean[G.V()];
		from=new int[G.V()];
		ord=new int[G.V()];
		for(int i=0;i<G.V();i++){
			visited[i]=false;
			from[i]=-1;
			ord[i]=-1;
		}
		this.s=s;
		Queue<Integer> queue=new LinkedList<Integer>();
		queue.add(s);
		visited[s]=true;
		ord[s]=0;
		while(!queue.isEmpty()){
			int v=queue.remove();
			for(int i:G.adj(v)){
				if(!visited[i]){
					queue.add(i);
					visited[i]=true;
					from[i]=v;
					ord[i]=ord[v]+1;
				}
			}
		}
	}
	public boolean hasPath(int w){
		if(w<0||w>=G.V()){
			throw new IllegalArgumentException("节点不存在");
		}
		return visited[w]&&ord[w]!=-1;
	}
	public Vector<Integer> path(int w){
		if(!hasPath(w)){
			throw new IllegalArgumentException("此路不通");
		}
		Stack<Integer> stack=new Stack<>();
		int p=w;
		while(p!=-1){
			stack.push(p);
			p=from[p];
		}
		Vector<Integer> vect=new Vector<Integer>();
		while(!stack.isEmpty()){
			vect.add(stack.pop());
		}
		return vect;
	}
	public void showPath(int w){
		if(!hasPath(w)){
			throw new IllegalArgumentException("此路不通");
		}
		Vector<Integer> vect=path(w);
		for(int i=0;i<vect.size();i++){
			System.out.print(vect.get(i));
			if(i==vect.size()-1){
				System.out.println();
			}else{
				System.out.print("->");
			}
		}
	}
	public int length(int w){
		if(w<0||w>=G.V()){
			throw new IllegalArgumentException("节点不存在");
		}
		return ord[w];
	}
}

找寻从V-W之间的路径


import java.util.Stack;
import java.util.Vector;

import javax.swing.plaf.synth.SynthSpinnerUI;

public class Path {
	private Graph G;
	private int s;	//起点
	private boolean[] visited;
	private int[] from;	//每个节点的来源 开始都是-1
	//在图graph中,以s为起点开始查路径
	public Path(Graph graph,int s) {
		this.G=graph;
		if(s<0||s>=G.V()){
			throw new IllegalArgumentException("节点不存在");
		}
		visited=new boolean[G.V()];
		from=new int[G.V()];
		for(int i=0;i<G.V();i++){
			visited[i]=false;
			from[i]=-1;
		}
		this.s=s;
		dfs(s);
	}
	private void dfs(int v) {
		visited[v]=true;
		for(int i:G.adj(v)){
			if(!visited[i]){
				from[i]=v;
				dfs(i);
			}
		}
	}
	//验证从v到w是否有路径
	public boolean hasPath(int w){
		if(w<0||w>=G.V()){
			throw new IllegalArgumentException("节点不存在");
		}
		return visited[w];
	}
	//返回从v-w的所经过节点的路径
	public Vector<Integer> path(int w){
		if(!hasPath(w)){
			throw new IllegalArgumentException("此路不通");
		}
		Stack<Integer> stack=new Stack<>();
		int p=w;
		while(p!=-1){
			stack.push(p);
			p=from[p];
		}
		Vector<Integer> vect=new Vector<>();
		while(!stack.isEmpty()){
			vect.add(stack.pop());
		}
		return vect;
	}
	//打印从v-w之间的路径
	public void showPath(int w){
		if(!hasPath(w)){
			throw new IllegalArgumentException("此路不通");
		}
		Vector<Integer> vect=path(w);
		for(int i=0;i<vect.size();i++){
			System.out.print(vect.get(i));
			if(i==vect.size()-1){
				System.out.println();
			}else{
				System.out.print("->");
			}
		}
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值