【算法导论-37】Graph的Java实现

前言

之前的博客“【算法导论-35】图算法JGraphT开源库介绍”中提到的开源版本的Graph库。然而,继续《算法导论》的学习必须自己实现Graph。所以,放弃使用该库,实现自己的Graph类。
注意,本篇博客紧密结合《算法导论》第22章,深度优先、广度优先、拓扑排序算法都取自相关章节的伪代码,这里不再讲解相关原理。
#Graph的实现
基础的Graph类的实现包括以下:
☆支持有向图和无向图两种类型;
☆支持泛型;
☆支持深度优先搜索和广度优先搜索;
☆支持拓扑排序;
在后续章节中,还会涉及到带权重的Graph,到时候再升级。
首先是Color类,这是在深度优先搜索和广度优先搜索时用到的Vertex属性,这里才有enum类型。

public enum Color {
	BLACK,WHITE,GRAY
}

Vertex类包含了邻接表(集合表示),以及深度优先、广度优先搜索的初次发现和结束的时间。

import java.util.LinkedHashSet;
import java.util.Set;

/**
 * <p>
 * Graph的顶点 参考:《算法导论》22.2节.
 * <p/>
 * <p>
 * created by 曹艳丰 2016-09-05
 * <p/>
 * */
public class Vertex<T> {
	public Color color;
	public int distance;
	public Vertex<T> parent;
	public T value;
	public int discover,finish;//深度优先搜索的第一次发现和结束的时间
	public Set<Vertex<T>> adjacencyVertices;// 邻接表,采用集合来表示

	public Vertex(T t) {
		// TODO Auto-generated constructor stub
		color = Color.WHITE;
		distance = Integer.MAX_VALUE;
		parent = null;
		value = t;
		adjacencyVertices = new LinkedHashSet<Vertex<T>>();
		discover=finish=0;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((value == null) ? 0 : value.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Vertex<?> other = (Vertex<?>) obj;
		if (value == null) {
			if (other.value != null)
				return false;
		} else if (!value.equals(other.value))
			return false;
		return true;
	}
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		StringBuilder builder=new StringBuilder();
		builder.append(value);
		return builder.toString();
	}

}

有向图和无向图的边不一样,提供一个超类Edge。

/**
 * <p>
 * Graph的顶点
 * 参考:《算法导论》22.2节.
 * <p/>
 * <p>
 * created by 曹艳丰
 * 2016-09-05
 * <p/>
 * */
public abstract class Edge<T> {
	public T source ,target;
	public Edge(T source ,T target) {
		// TODO Auto-generated constructor stub
		this.source=source;
		this.target=target;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((source == null) ? 0 : source.hashCode());
		result = prime * result + ((target == null) ? 0 : target.hashCode());
		return result;
	}
	/**
	 * 有向边和无向边的equal函数不同.有向边要求source→target顺序,无向边不要求
	 * */
	@Override
	public abstract boolean equals(Object obj); 
}

无向图的边UndirectedEdge。

/**
 * <p>
 * 无向Graph的Edge实现
 * <p/>
 * <p>
 * 2016-09-05
 * <p/>
 * */
public class UndirectedEdge<T> extends Edge<T> {

	public UndirectedEdge(T source, T target) {
		super(source, target);
		// TODO Auto-generated constructor stub
	}
	@Override
	public boolean equals(Object obj) {
		// TODO Auto-generated method stub
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Edge<?> other = (Edge<?>) obj;
		
		if (source == null) {
			if (other.source != null&&other.target!=null)
				return false;
		} else if (!source.equals(other.source)&&!source.equals(other.target))
			return false;
		if (target == null) {
			if (other.target != null&&other.source!=null)
				return false;
		} else if (!target.equals(other.target)&&!target.equals(other.source))
			return false;
		return true;
	}

}

有向图的边DirectedEdge。

/**
 * <p>
 * 有向Graph的Edge实现
 * <p/>
 * <p>
 * created by 曹艳丰
 * 2016-09-05
 * <p/>
 * */
public class DirectedEdge<T> extends Edge<T>{

	public DirectedEdge(T source, T target) {
		super(source, target);
		// TODO Auto-generated constructor stub
	}
	
	/**
	 *有向边,source和target必须对应相等 
	 * */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Edge<?> other = (Edge<?>) obj;
		if (source == null) {
			if (other.source != null)
				return false;
		} else if (!source.equals(other.source))
			return false;
		if (target == null) {
			if (other.target != null)
				return false;
		} else if (!target.equals(other.target))
			return false;
		return true;
	}
}

然后到了有向图和无向图的超类Graph。Graph提供了邻接表和邻接矩阵两种形式。为了支持深度优先、广度优先搜索,提供了time变量。

import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import java.util.Stack;

/**
 * <p>
 * Graph的实现
 * <p/>
 * <p>
 * 参考:《算法导论》22.1节.
 * <p/>
 * <p>
 * <p/>
 * <p>
 * 2016-09-05
 * <p/>
 * */
public abstract class Graph<T> {
	protected int[][] adjacencyMatrix;// 邻接矩阵
	protected int size = 0;// 定点总数
	public Set<Vertex<T>> vertices;// 所有节点
	public Set<Edge<T>> edges;// 所有边
	private int time;
	private Stack<Vertex<T>> topologicalStack;//用于拓扑排序
	public Graph() {
		// TODO Auto-generated constructor stub
		adjacencyMatrix = new int[size][size];
		vertices = new LinkedHashSet<Vertex<T>>();
		edges = new LinkedHashSet<Edge<T>>();
		topologicalStack=new Stack<Vertex<T>>();
	}

	public Graph<T> addVertex(T t) {
		Vertex<T> vertex = new Vertex<T>(t);
		// 如果已经包含该节点,抛出异常
		if (vertices.contains(vertex)) {
			throw new IllegalArgumentException("该节点已经存在,不能再添加!");
		}
		size++;
		vertices.add(vertex);
		adjustMatrix(t);
		return this;
	}

	// 添加边,t1-t2
	public abstract void addEdge(T t1, T t2);

	// 调整邻接矩阵
	private void adjustMatrix(T t) {
		int[][] tempyMatrix = new int[size][size];
		for (int i = 0; i < size - 1; i++) {
			for (int j = 0; j < size - 1; j++) {
				tempyMatrix[i][j] = adjacencyMatrix[i][j];
			}
		}
		adjacencyMatrix = tempyMatrix;
	}

	protected void setMatrixValue(int row, int column) {
		adjacencyMatrix[row][column] = 1;
	}

	// 以邻接矩阵的形式打印Graph
	public void printAdjacencyMatrix() {
		System.out.println("邻接矩阵形式:");
		for (int[] is : adjacencyMatrix) {
			for (int i : is) {
				System.out.print(i);
				System.out.print(" ");
			}
			System.out.println();
		}
	}

	// 以邻接表的形式打印Graph
	public void printAdjacencyVertices() {
		System.out.println("邻接表形式:");
		for (Vertex<T> vertex : vertices) {
			System.out.print(vertex);
			for (Vertex<T> vertex2 : vertex.adjacencyVertices) {
				System.out.print("→");
				System.out.print(vertex2);
			}
			System.out.println();
		}
	}

	protected Vertex<T> isContainVertext(T t) {
		for (Vertex<T> v : vertices) {
			if (v.value.equals(t)) {
				return v;
			}
		}
		return null;
	}

	protected Edge<T> isContainEdge(T t1, T t2) {
		for (Edge<T> edge : edges) {
			if (edge.source.equals(t1) && edge.target.equals(t2)) {
				return edge;
			}
		}
		return null;
	}

	// 广度优先搜索,广度优先搜索其实是利用了一个队列
	// 参考《算法导论》22.2节伪代码
	public void printBFS(T t) {
		Vertex<T> vertex = isContainVertext(t);
		if (vertex == null) {
			throw new IllegalArgumentException("不能包含该节点,不能进行广度优先搜索!");
		}
		reSet();
		System.out.println("广度优先搜索:");
		vertex.color = Color.GRAY;
		vertex.distance = 0;
		Queue<Vertex<T>> queue = new LinkedList<Vertex<T>>();
		queue.offer(vertex);
		while (queue.size() > 0) {
			Vertex<T> u = queue.poll();
			for (Vertex<T> v : u.adjacencyVertices) {
				if (v.color == Color.WHITE) {
					v.color = Color.GRAY;
					v.distance = u.distance + 1;
					v.parent = u;
					queue.offer(v);
				}
			}
			u.color = Color.BLACK;
			System.out.print(u);
			System.out.print("→");
		}
		System.out.println();
	}
	public void printDFS(T t){
		Vertex<T> vertex = isContainVertext(t);
		if (vertex == null) {
			throw new IllegalArgumentException("不能包含该节点,不能进行广度优先搜索!");
		}
		reSet();
		System.out.println("深度优先搜索:");
		time=0;
		dfsVisit(vertex);
		for (Vertex<T> u : vertices) {
			if (u.equals(vertex)) {
				continue;
			}
			if (u.color==Color.WHITE) {
				dfsVisit(u);
			}
		}
		System.out.println();
	}
	private void dfsVisit(Vertex<T> u){
		System.out.print(u);
		System.out.print("→");
		time++;
		u.discover=time;
		u.color=Color.GRAY;
		for (Vertex<T> v : u.adjacencyVertices) {
			if (v.color==Color.WHITE) {
				v.parent=u;
				dfsVisit(v);
			}
		}
		u.color=Color.BLACK;
		time++;
		u.finish=time;
		
		topologicalStack.push(u);
		
	}
	/**
	 * 打印出拓扑结构
	 * */
	public void printTopology(T t){
		if (topologicalStack.size()==0) {
			printDFS(t);
		}
		while (topologicalStack.size()>0) {
			System.out.print(topologicalStack.pop());
			System.out.print("→");
		}
		System.out.println();
		
	}
	private void reSet(){
		for (Vertex<T> vertex : vertices) {
			vertex.color=Color.WHITE;
		}
		topologicalStack.clear();
	}

}

有向图和无向图仅仅添加边时不一样。
☆无向图添加边时,需要修改两个顶点双方的邻接矩阵和邻接表,而有向图添加边时,只修改起点的邻接矩阵和邻接表。
☆有向图单个定点可以有一条边指向自己,而无向图不能这样“自己指向自己”;
无向图如下,每次添加边时,都更新两个顶点对应的邻接表和邻接矩阵。

/**
 * <p>
 * 无向Graph的实现
 * 参考:《算法导论》22.1节.
 * <p/>
 * <p>
 * 2016-09-05
 * <p/>
 * */

public class UndirectedGraph<T> extends Graph<T> {

	@Override
	public void addEdge(T source, T target) {
		// TODO Auto-generated method stub
		if (source.equals(target)) {
			throw new IllegalArgumentException("无向图不能包含自旋!");
		}
		Vertex<T> sourceVertex=isContainVertext(source);
		Vertex<T> targetVertex=isContainVertext(target);
		if (sourceVertex==null) {
			throw new IllegalArgumentException("不包含起始节点!");
		}
		if (targetVertex==null) {
			throw new IllegalArgumentException("不包含终端节点!");
		}
		if (isContainEdge(source, target)!=null||isContainEdge(target,source)!=null) {
			throw new IllegalArgumentException("重复添加该边!");
		}
		// 添加新的边
		edges.add(new UndirectedEdge<T>(source, target));
		int row = 0, column = 0;
		int counter = 0;
		int i=0;
		for (Vertex<T> vertex : vertices) {
			if (vertex.value.equals(source)) {
				vertex.adjacencyVertices.add(targetVertex);//更新邻接表
				row = i;
				counter++;
				if (counter == 2) {
					setMatrixValue(row, column);// 设置邻接矩阵的值
					setMatrixValue(column, row);// 设置邻接矩阵的值
					break;
				}
			}else if (vertex.value.equals(target)) {
				vertex.adjacencyVertices.add(sourceVertex);//更新邻接表
				column = i;
				counter++;
				if (counter == 2) {
					setMatrixValue(row, column);
					setMatrixValue(column, row);
					break;
				}
			}
			i++;
		}
	}
}

有向图如下,每次添加边的时候,都更新起点的邻接表和邻接矩阵。

/**
 * <p>
 * 有向Graph的实现
 * 参考:《算法导论》22.1节.
 * <p/>
 * <p>
 * 2016-09-05
 * <p/>
 * */
public class DirectedGraph<T> extends Graph<T> {

	@Override
	public void addEdge(T source, T target) {
		// TODO Auto-generated method stub
		Vertex<T> sourceVertex=isContainVertext(source);
		Vertex<T> targetVertex=isContainVertext(target);
		if (sourceVertex==null) {
			throw new IllegalArgumentException("不包含起始节点!");
		}
		if (targetVertex==null) {
			throw new IllegalArgumentException("不包含终端节点!");
		}
		if (isContainEdge(source, target)!=null) {
			throw new IllegalArgumentException("重复添加该边!");
		}
		// 添加新的边
		edges.add(new DirectedEdge<T>(source, target));
		int row = 0, column = 0;
		int counter = 0;
		int i=0;
		for (Vertex<T> vertex : vertices) {
			if (vertex.value.equals(source)&&source.equals(target)) {
				vertex.adjacencyVertices.add(targetVertex);//更新邻接表
				row = i;
				column=i;
				setMatrixValue(row, column);// 设置邻接矩阵的值
				break;
				
			}else if (vertex.value.equals(source)) {
				vertex.adjacencyVertices.add(targetVertex);//更新邻接表
				row = i;
				counter++;
				if (counter == 2) {
					setMatrixValue(row, column);// 设置邻接矩阵的值
					break;
				}
			}else if (vertex.value.equals(target)) {
				column = i;
				counter++;
				if (counter == 2) {
					setMatrixValue(row, column);
					break;
				}
			}
			i++;
		}
		
	}

}

#Graph测试
有了这几个类之后,可以进行一系列测试。

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		testUndirectedGraph();
		testDirectedGraph();
		testBFS();
		testDFS();
		testTopolgy();
	}
	/**
	 * 《算法导论》22.1节图22.1
	 * */
	private static void testUndirectedGraph(){
		Graph<String> graph=new UndirectedGraph<String>();
		graph.addVertex("1");
		graph.addVertex("2");
		graph.addVertex("3");
		graph.addVertex("4");
		graph.addVertex("5");
		graph.addEdge("1","2");
		graph.addEdge("5","1");
		graph.addEdge("2","3");
		graph.addEdge("2","4");
		graph.addEdge("2","5");
		graph.addEdge("4","3");
		graph.addEdge("4","5");
		
		graph.printAdjacencyMatrix();
		graph.printAdjacencyVertices();
	}
	/**
	 * 《算法导论》22.1节图22.1
	 * */
	private static void testDirectedGraph(){
		Graph<Integer> graph2=new DirectedGraph<Integer>();
		graph2.addVertex(1).addVertex(2).addVertex(3).addVertex(4).addVertex(5).addVertex(6);
		graph2.addEdge(1, 2);
		graph2.addEdge(1, 4);
		graph2.addEdge(2, 5);
		graph2.addEdge(3, 5);
		graph2.addEdge(3, 6);
		graph2.addEdge(4, 2);
		graph2.addEdge(5, 4);
		graph2.addEdge(6, 6);
		graph2.printAdjacencyMatrix();
		graph2.printAdjacencyVertices();
	}
	/**
	 * 《算法导论》22.2节图22.3
	 * 
	 * */
	private static void testBFS(){
		Graph<String> graph=new UndirectedGraph<String>();
		graph.addVertex("s").addVertex("r").addVertex("v")
		.addVertex("w").addVertex("t").addVertex("x").addVertex("u").addVertex("y");
		
		graph.addEdge("v","r");
		graph.addEdge("r","s");
		graph.addEdge("s","w");
		graph.addEdge("w","t");
		graph.addEdge("w","x");
		graph.addEdge("t","x");
		graph.addEdge("t","u");
		graph.addEdge("x","u");
		graph.addEdge("x","y");
		graph.addEdge("u","y");
		graph.printBFS("x");
		
	}
	private static void testDFS(){
		Graph<String> graph2=new DirectedGraph<String>();
		graph2.addVertex("u").addVertex("v").addVertex("w").addVertex("x").addVertex("y").addVertex("z");
		graph2.addEdge("u", "x");
		graph2.addEdge("u", "v");
		graph2.addEdge("x", "v");
		graph2.addEdge("v", "y");
		graph2.addEdge("y", "x");
		graph2.addEdge("w", "y");
		graph2.addEdge("w", "z");
		graph2.addEdge("z", "z");
		graph2.printDFS("u");
		
	}
	/**
	 * 《算法导论》22.4节图22.7
	 * 
	 * */
	private static void testTopolgy(){
		Graph<String> graph2=new DirectedGraph<String>();
		graph2.addVertex("undershorts").addVertex("pants").addVertex("belt").
		addVertex("shirt").addVertex("tie").addVertex("jacket").addVertex("socks").
		addVertex("watch").addVertex("shoes");
		graph2.addEdge("undershorts", "pants");
		graph2.addEdge("undershorts", "shoes");
		graph2.addEdge("pants", "belt");
		graph2.addEdge("belt", "jacket");
		graph2.addEdge("pants", "shoes");
		graph2.addEdge("shirt", "tie");
		graph2.addEdge("shirt", "belt");
		graph2.addEdge("tie", "jacket");
		graph2.addEdge("socks", "shoes");
		graph2.printTopology("shirt");
	}

}

测试结果如下。

邻接矩阵形式:
0 1 0 0 1 
1 0 1 1 1 
0 1 0 1 0 
0 1 1 0 1 
1 1 0 1 0 
邻接表形式:
1→2→5
2→1→3→4→5
3→2→4
4→2→3→5
5→1→2→4
邻接矩阵形式:
0 1 0 1 0 0 
0 0 0 0 1 0 
0 0 0 0 1 1 
0 1 0 0 0 0 
0 0 0 1 0 0 
0 0 0 0 0 1 
邻接表形式:
1→2→4
2→5
3→5→6
4→2
5→4
6→6
广度优先搜索:
x→w→t→u→y→s→r→v→
深度优先搜索:
u→x→v→y→w→z→
深度优先搜索:
shirt→tie→jacket→belt→undershorts→pants→shoes→socks→watch→
watch→socks→undershorts→pants→shoes→shirt→belt→tie→jacket→

注意:深度优先、广度优先以及拓扑排序与《算法导论》中的结果不完全一直,这是由邻接表中顶点的顺序不完全同书上一致导致的。例如,最后的拓扑排序为:
watch→socks→undershorts→pants→shoes→shirt→belt→tie→jacket→
而书上的为
socks→undershorts→pants→shoes→watch→shirt→belt→tie→jacket→
只有watch不一致,而watch放到哪里都是可以的。

请我喝咖啡

如果觉得写得不错,可以扫描我的微信二维码请我喝咖啡哦~

在这里插入图片描述
或者点击 打赏地址 请我喝杯茶~

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值