仅个人常用结构实现-没有复杂的算法:将图问题先转换成个人熟悉的图结构
点的类结构代码:
public class Node { //点类
public int val; //具体值
public int in; //多少点连向它
public int out; //多少点被它连着
public ArrayList<Node> nexts; //它能直接到达的点
public ArrayList<Side> sides; //它能使用的边
public Node(int val) {
this.val = val;
this.in = 0;
this.out = 0;
this.nexts = new ArrayList<>();
this.sides = new ArrayList<>();
}
//重写equals和hashcode,使点和点的比较,只对val判定
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Node node = (Node) o;
return val == node.val;
}
@Override
public int hashCode() {
return Objects.hash(val);
}
}
边的类结构代码:
public class Side {
public int weight; //边的权重
public Node form; //这条边由谁连
public Node to; //这条边连向谁
public Side() {
}
public Side(int weight, Node form, Node to) {
this.weight = weight;
this.form = form;
this.to = to;
}
}
图的结构代码:
//图
public class Picture {
Picture p;
public ArrayList<Node> nodes; //点
public ArrayList<Side> sides; //边
public Picture() {
}
public Picture(Node nodes, Side sides) {
this.nodes = new ArrayList<>();
this.sides = new ArrayList<>();
}
//初始化图-输入一个二阶矩阵,trista[a][b]中,trista[a][0]:form出发点,trista[a][1]:form到达点,trista[a][2]:权重值,
public Picture initialization (int[][] trista){
if(p==null){
//图不存在就创建一个新图
p = new Picture();
}
//遍历所有二阶数组矩阵
for (int i = 0; i <trista.length; i++) {
//先创建2个点
Node thisform = new Node(trista[i][0]);
Node thisto = new Node(trista[i][1]);
//判断图中是否存在这2个点, 已重写equals、hashcode,只比node.val;
if(!p.nodes.contains(thisform)){
nodes.add(thisform);
}
if(!p.nodes.contains(thisto)){
nodes.add(thisto);
}
//重新拿出该图的node集合中的对应点-可能之前图中已经存在对应点,那么就应该使用之前的点
thisform = p.nodes.get(trista[i][0]);
thisto = p.nodes.get(trista[i][1]);
//创建对应的边-每条边都是新的-不需要在sides集合判断
Side thisside = new Side(trista[i][2], thisform, thisto);
p.sides.add(thisside);
//同时,进行数据的更新,比如出发点的out、next、side
thisform.out++;
thisto.in++;
thisform.nexts.add(thisto);
thisform.sides.add(thisside);
}
return p;
}
//广度优先遍历
public void bfs(Node node){
//队列-实现广度优先遍历
Queue<Node> queue = new LinkedList<>();
HashSet<Node> set = new HashSet<>();
queue.add(node);
set.add(node);
while (!queue.isEmpty()){
//弹栈即打印
Node cur = queue.poll();
System.out.println(cur.val);
//遍历当前弹栈点的所有邻点-set保证所有点不会第二次添加到队列中
for (Node node1:cur.nexts) {
if(!set.contains(node1)){
queue.add(node1);
set.add(node1);
}
}
}
}
//深度优先遍历
public void dfs(Node node){
//同理,用栈先进后出实现深度优先,set保证点不进入第二次
Stack<Node> stack = new Stack<>();
HashSet<Node> set = new HashSet<>();
stack.push(node);
System.out.println(node.val);
//进栈即打印
set.add(node);
while (!stack.isEmpty()){
Node cur = stack.pop();
for (Node c: cur.nexts) {
if(!set.contains(c)){
/**
*注意,是深度优先,所以我们找到一个邻点,就先去邻点访问其nexts,所以先将cur进栈,然后再让邻点cur进栈,让邻栈先遍历
*/
stack.add(cur); //有点类似状态回溯
stack.add(c);
System.out.println(c.val);
set.add(c);
//然后先退出此次的遍历
break;
}
}
}
}
}
拓扑遍历
//图的拓扑结构遍历---当点与点之前存在 推论关系(必须先做A、B..才能做C),怎么遍历打印
public void topology(Picture p){
//记录每个点和它的入度-入度为0即可遍历(装入queue容器)
HashMap<Node, Integer> map = new HashMap<>();
Queue<Node> queue = new LinkedList<>();
for (Node node:p.nodes) {
int in = node.in;
if(in == 0){
queue.add(node);
}else {
map.put(node,in);
}
}
//最终的打印容器
ArrayList<Node> list = new ArrayList<>();
while (!queue.isEmpty()){
Node poll = queue.poll();
list.add(poll);
//对每个邻节点,入度-1
for (Node node:poll.nexts) {
int i = node.in - 1;
if(i==0){
queue.add(node);
}else {
map.put(node,i);
}
}
}
}