题目:给定带权有向图生成最小生成树
思路:方法1 使用并查集按照边权值递增合并节点,直到所有节点在同一个并查集 方法2:任意节点 取相邻权值最小的边,将该边临接节点拉入已遍历集合 更新已遍历集合和未遍历集合最小权值集合,遍历完所有节点得到最小生成树
// kruskal算法求图的最小生成树
public static Set<Edge> kruskalMST(Graph graph) {
// 图各个节点看作独立的并查集
UnionFound240208 UF = new UnionFound240208(graph.nodes.values());
// 将边放入优先队列 权重递增
PriorityQueue<Edge> queue = new PriorityQueue<>(new Comparator<Edge>() {
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight - o2.weight;
}
});
queue.addAll(graph.edges);
// 遍历队列直到为空
Set<Edge> ans = new HashSet<>();
while (!queue.isEmpty()){
Edge e = queue.poll();
// 如果边两端是否在同一个并查集里面,如果不是将两个节点所在并查集合并为一个并查集
if(!UF.isSame(e.from,e.to)){
ans.add(e);
UF.union(e.from,e.to);
}
}
return ans;
}
// 并查集
public static class UnionFound240208{
private static HashMap<Node, Node> parent;
private static HashMap<Node,Integer> sizeMap;
UnionFound240208(Collection<Node> list){
for (Node node : list) {
parent.put(node,node);
sizeMap.put(node,1);
}
}
// 是否为相同并查集
public boolean isSame(Node a,Node b){
Node pa = findParent(a);
Node pb = findParent(b);
return pa == pb;
}
// 两个Node节点合并
public void union(Node a,Node b){
if(!isSame(a,b)){
// 少数服从多数
if(sizeMap.get(a)< sizeMap.get(b)){
parent.put(a,b);
sizeMap.put(b, sizeMap.get(a)+ sizeMap.get(b));
sizeMap.remove(a);
}else{
parent.put(b,a);
sizeMap.put(a, sizeMap.get(a)+ sizeMap.get(b));
sizeMap.remove(b);
}
}
}
// 查找给定节点所在并查集的祖先节点
public Node findParent(Node node){
Node cur = node;
ArrayList<Node> list = new ArrayList<>();
while (cur != parent.get(cur)){
list.add(cur);
cur = parent.get(cur);
}
// 到这里 cur为node 所在并查集的祖先节点
// 压缩list路径节点为公共祖先节点
for (Node n : list) {
parent.put(n,cur);
}
return cur;
}
// 多少个相互独立的并查集
public Integer getUnions(){
return sizeMap.size();
}
}
// 方法2
public static Set<Edge> primMST240208(Graph graph) {
Set<Node> visited = new HashSet<>();
PriorityQueue<Edge> queue = new PriorityQueue<>(new Comparator<Edge>() {
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight - o2.weight;
}
});
Set<Edge> ans = new HashSet<>();
// 最外边兜底作用,防止没有遍历所有节点
for (Node node : graph.nodes.values()) {
if(!visited.contains(node)){
// 每个最小生成树初始化
visited.add(node);
queue.addAll(node.edges);
while (!queue.isEmpty()){
Edge edge = queue.poll();
if(!visited.contains(edge.to)){
visited.add(edge.to);
ans.add(edge);
queue.addAll(edge.to.edges);
}
}
}
}
return ans;
}
题目:给定图及指定节点,返回Map,key为图所有节点,value为指定节点到key代表节点的最短距离
思路:维护已遍历节点到未遍历节点的节点和最短距离映射,将最短距离关联节点纳入到已遍历节点集合,重复上面操作直到所有完成所有节点遍历
public static HashMap<Node, Integer> dijkstra240209(Node from) {
// PC
HashMap<Node,Integer> ans = new HashMap<>();
HashMap<Node, Integer> disMap = new HashMap<>();
HashSet<Node> selected = new HashSet<>();
disMap.put(from,0);
Node minNode = getNextNode(disMap, selected);
if(minNode == null){
return ans;
}
while (minNode != null){
// 已遍历节点和未遍历节点的最短距离 然后加入已遍历节点集合
selected.add(minNode);
Integer base = disMap.get(minNode);
// 更新已遍历节点和未遍历节点最短距离
for (Edge edge : minNode.edges) {
if(selected.contains(edge.to)){
disMap.put(edge.to,Math.min(base+edge.weight,disMap.get(edge.to)));
}else{
disMap.put(edge.to,base+edge.weight);
}
}
minNode = getNextNode(disMap,selected);
}
return disMap;
}
// disMap代表的是已遍历节点到未遍历节点的最小距离,key集合是已遍历节点集合子集合
// 所以不能通过是否在disMap里面判断node是否已经遍历
// 得到已遍历节点和未遍历节点最短edge关联的节点
public static Node getNextNode(HashMap<Node,Integer> disMap,HashSet<Node> selected){
int minlen = Integer.MAX_VALUE;
Node minNode = null;
for (Map.Entry<Node, Integer> entry : disMap.entrySet()) {
Node node = entry.getKey();
Integer weight = entry.getValue();
if(!selected.contains(node) && weight<minlen){
minNode = node;
minlen = weight;
}
}
return minNode;
}