图
自定义转换接口
由于图结构的定义灵活多变,不可能每次遇到一个新的形式再去思考如何解题,所以我们需要一个接口,将所有的形式的图转变为我们熟悉的形式。
//图
public class Graph {
//点集
public HashMap<Integer,Node> nodes;
//边集
public HashSet<Edge> edges;
public Graph() {
this.nodes=new HashMap<Integer,Node>();
this.edges=new HashSet<Edge>();
}
}
//点
public class Node {
public int value;
public int in;//入度
public int out;//出度
public ArrayList<Node> nexts=new ArrayList();//相邻点
public ArrayList<Edge> edges=new ArrayList();//属于的边
public Node(int value, int in, int out) {
super();
this.value = value;
this.in = in;
this.out = out;
}
public Node(int value) {
super();
this.value = value;
}
}
//边
public class Edge {
public Node from;
public Node to;
public int weight;//权重
public Edge(Node from, Node to, int weight) {
super();
this.from = from;
this.to = to;
this.weight = weight;
}
}
//装换
public class CreateGraph {
public static Graph createGraph(Integer[][] matrix) {
Graph graph=new Graph();
//对矩阵进行转化
for(int i=0;i<matrix.length;i++) {//[i][0]——from节点,[i][1]——to节点,[i][3]——权重
int fromNode=matrix[i][0];
int toNode=matrix[i][1];
int weight=matrix[i][2];
//判断图的点集中是否已有此节点
if(!graph.nodes.containsKey(fromNode)) {
graph.nodes.put(fromNode, new Node(fromNode));
}
//判断图的点集中是否已有此节点
if(!graph.nodes.containsKey(toNode)) {
graph.nodes.put(toNode,new Node(toNode));
}
//图中无,可以进行添加节点,并设置节点值
Node fNode=graph.nodes.get(fromNode);
Node tNode=graph.nodes.get(toNode);
fNode.out=fNode.out++;
fNode.nexts.add(tNode);
tNode.in=tNode.in++;
tNode.nexts.add(fNode);
//添加边集
Edge edge=new Edge(fNode,tNode,weight);
graph.edges.add(edge);
}
return graph;
}
}
图宽度优先算法
图中每个点多有相邻点,对一个点操作后需要引入下一个点,这样可能会再次对操作过的点又进行操作。所以我们需要一个界定条件。使用set集合作为界定条件,对一个点操作后将它放入set集合中,如果set集合中出现这个点说明已经操作过,不需要在操作。使用队列queue结构实现宽度优先算法。
//宽度优先算法
public class Bfs {
public static void travleGraph_bfs(Node node) {
if(node==null) {
System.out.println("空图");
return;
}
Queue<Node> queue=new LinkedList();
HashSet<Node> isExist=new HashSet();
queue.add(node);
isExist.add(node);
while(!queue.isEmpty()) {
Node curNode=queue.poll();
System.out.print(curNode.value+" ");
//邻接点入队
for(Node next:curNode.nexts) {
//判断邻接点是否已经入队过
if(!isExist.contains(next)) {
queue.add(next);
isExist.add(next);
}
}
}
}
}
图的深度优先算法
当每到一个点,我们就取这个点的相邻节点中的一个点,对这个节点的相邻点继续取它的相邻点一直取到低,其中有两个问题:①如果有循环,会一直循环下去②相邻点有多个,遍历完一个,如果再走另一个
解决:
①需要一个界定条件,使用set集合作为界定条件。遍历一个就放入set集合。
②使用栈去存储节点,当我们弹出节点时判断它的相邻节点是否存在如果存在,就将弹出的节点重新压回去在将它的相邻节点压入。这个就保持了节点的状态,下次还能遍历到它的其他相邻节点。
public class Dfs {
public static void travleGraph_bfs(Node node) {
if(node==null) {
System.out.println("空图");
return;
}
Stack<Node> stack=new Stack();
HashSet<Node> set=new HashSet();//界定条件
stack.push(node);
set.add(node);
System.out.print(node.value+" ");
while(!stack.isEmpty()) {
Node curNode=stack.pop();
//遍历邻点
for(Node next:curNode.nexts) {
//判断是否遍历过此点
if(!set.contains(next)) {
stack.push(curNode);//重新入栈
stack.push(next);//邻点入栈
set.add(next);//表示邻点已经遍历过
System.out.print(next.value+" ");
break;
}
}
}
}
}
图的拓扑排序
拓扑结构简单理解为,前一个条件完成后下一个条件才可进行。
实现思路:每个节点都有入度与出度,可以从这个方向入手实现,使用队列结构对节点进行操作。
public class TopologySort {
public static List<Node> sortedTopology(List<Node> nodes){
if(nodes.isEmpty()) {
System.out.println("空图");
return nodes;
}
Queue<Node> queue=new LinkedList();
ArrayList<Node> list=new ArrayList();
//寻找list中第一批入度为0的点,对这些点入队
for(Node node:nodes) {
if(node.in==0) {
queue.add(node);
}
}
while(!queue.isEmpty()) {
Node curNode=queue.poll();
list.add(curNode);//将出队的节点入列表
curNode.out--;//对此节点出度减一
System.out.print(curNode.value+" ");
//寻找出队点的下一个点
for(Node next:curNode.nexts) {
next.in--;//将寻找到的点入度减一
//判断这些邻点有没有入度为0的
if(next.in==0) {
queue.add(next);
}
}
}
return list;
}
}
//寻找出队点的下一个点
for(Node next:curNode.nexts) {
next.in--;//将寻找到的点入度减一
//判断这些邻点有没有入度为0的
if(next.in==0) {
queue.add(next);
}
}
}
return list;
}
}