图的结构:
//图
public class Graph {
public HashMap<Integer,Node> nodes;
public HashSet<Edge> edges;
public Graph() {
this.nodes = new HashMap();
this.edges = new HashSet();
}
}
=====================================================================================
//点
public class Node {
public int value;
public int in;//出这条点的边
public int out;//进入这个点的边
public ArrayList<Node> nexts;//这个点出去的边连接的点
public ArrayList<Edge> edges;//这个点出去的边
public Node(int value) {
this.value = value;
this.in = 0;
this.out = 0;
this.nexts = new ArrayList();
this.edges = new ArrayList();
}
}
=====================================================================================
//边
public class Edge {
public int weight;//权重
public Node from;//来自哪个点
public Node to;//去哪个点
public Edge(int weight, Node from, Node to) {
this.weight = weight;
this.from = from;
this.to = to;
}
}
- 图的宽度优先算法,和树的非常像,只是需要一个HashSet来记录是否重复即可:
//宽度优先遍历 和树很像就是需要避免死循环
public static void widthFirst(Node node){
if(node==null)
return;
Queue<Node> queue=new LinkedList<>();
HashSet<Node> set=new HashSet<>();
queue.add(node);
set.add(node);
while(!queue.isEmpty()){
Node temp=queue.poll();
System.out.println(temp.value);
for(int i=0;i<temp.nexts.size();i++){//取到相邻的节点
if(!set.contains(temp.nexts.get(i))){//如果不存在才放入队列做处理
queue.add(temp.nexts.get(i));
set.add(temp.nexts.get(i));
}
}
}
}
- 深度优先,与宽度优先相比只需要每次放入一个即可:
//深度优先 就是每次只放入一个需要的栈 不用一次性遍历完
public static void deepFirst(Node node){
if(node==null)
return;
Stack<Node> stack=new Stack<>();
HashSet<Node> set=new HashSet<>();
stack.push(node);
set.add(node);
System.out.println(node.value);
while(!stack.isEmpty()){
Node temp=stack.pop();
for(Node n:temp.nexts){
if(!set.contains(n)){
stack.push(temp);//再次放入栈 是为了让栈记录我们的路线 并且可以回溯 防止以前的节点有些节点没有走到
stack.push(n);
set.add(n);
System.out.println(n.value);
break;
}
}
}
}
- 拓扑排序,如果我们需要做一个项目,它里面有很多依赖包,依赖之间可能有相互依赖的关系,我们需要做出一个排序来先编译什么文件后编译什么文件,这样的排序的过程叫做拓扑排序,在图中即是存在一个有向图,而且没有循环依赖的关系,即没有环。
我们的思路就是首先找到此时没有入度(就是没有其他点指向它的点)的节点,然后处理它对其他节点的影响(就是让他们的入度-1),循环完成即可,代码如下:
public static void main(String[] args){
Node node1=new Node(1);
Node node2=new Node(2);
Node node3=new Node(3);
Node node4=new Node(4);
Node node5=new Node(5);
node1.in=0;node1.nexts.add(node2);node1.nexts.add(node3);node1.nexts.add(node5);
node2.in=1;node2.nexts.add(node3);
node3.in=2;node3.nexts.add(node4);node3.nexts.add(node5);
node4.in=1;node4.nexts.add(node5);
node5.in=3;
Graph graph=new Graph();
HashMap<Integer,Node> map=new HashMap();
map.put(1, node1);map.put(2, node2);map.put(3, node3);map.put(4, node4);map.put(5, node5);
graph.nodes=map;
sortedTopology(graph);
}
//拓扑排序
public static void sortedTopology(Graph graph){
//每个节点的剩余入度的个数和节点的对应关系
HashMap<Node,Integer> inMap=new HashMap();
//装如入度为0的节点
Queue<Node> zeroQueue=new LinkedList();
for(Node node:graph.nodes.values()){
inMap.put(node, node.in);
if(node.in==0){
zeroQueue.add(node);
}
}
//装结果的集合
LinkedList<Node> result=new LinkedList();
while(!zeroQueue.isEmpty()){
Node zero=zeroQueue.poll();
result.add(zero);
for(Node temp:zero.nexts){//去消除这个入度为0的点为后面的点带来的影响
inMap.put(temp, inMap.get(temp)-1);//入度减1
if(inMap.get(temp)==0){
zeroQueue.add(temp);
}
}
}
for(Node temp:result){
System.out.println(temp.value);
}
}
- Dijkstra算法,这个算法的想法就是 从一个节点开始,计算一次到它直接相连节点的最下距离,计算完过后,封锁这个节点,寻找刚刚已经计算的节点中距离最小的节点,丛它开始计算它直接相连节点的距离与之前的相比,将更小的值修改,重复这个操作,知道所有节点都已经走过了:
代码如下:
//是求从一个点开始 到其他所有点的最小值
public static HashMap<Node,Integer> dijkstra(Node head){
//一个map来装我们的节点 和它到头节点的最小距离
HashMap<Node,Integer> map=new HashMap();
//一个set用来装已经选完的节点
HashSet<Node> set=new HashSet();
map.put(head, 0);//head到当前节点的距离是0
Node minNode=getMinNodeAndNoSelect(map,set);
while(minNode!=null){//当还有节点没有被封锁的时候
List<Edge> nextEdges=minNode.edges;
for(Edge edge:nextEdges){//遍历所有该节点的可达节点 比较大小
Node toNode=edge.to;
if(!map.containsKey(toNode)){//该边所达到的点如果map中不存在 就代表上一次的点没有达到过这个节点
map.put(toNode, map.get(minNode)+edge.weight);//赋值 那么该点的距离就应该为 minNode对应的值 加上这条边的权值
}else{
map.put(toNode, Math.min(map.get(toNode), map.get(minNode)+edge.weight));
}
}
set.add(minNode);//将minNode放入已经搜查过的节点中
minNode=getMinNodeAndNoSelect(map,set);//重新找minNode
}
return map;//返回结果
}
//获取距离最小 而且没有被选中的节点
public static Node getMinNodeAndNoSelect(HashMap<Node,Integer> map,HashSet<Node> set){
Node minNode=null;
int min=Integer.MAX_VALUE;
for(Node node:map.keySet()){
if(map.get(node)<min&&!set.contains(node)){//值最小 同时没有被选过
minNode=node;
}
}
return minNode;
}