算法体系-图

一、 图的存储方式有很多种,固定自己的一种方式,到时候只需做结构的转化代码即可

  1. 图存储结构模板
//图结构
public class Graph {
    public HashMap<Integer, Node> nodes;
    public HashSet<Edge> edges;
}
//节点结构
public class Node {
    public int value;
    public int in;
    public int out;
    public ArrayList<Edge> edges;
    public ArrayList<Node> nexts;

    public Node(int value) {
        this.value = value;
        int in=0;
        int out=0;
        edges=new ArrayList<>();
        nexts=new ArrayList<>();
    }
}
//边结构
public class Edge {
    Node from;
    Node to;
    int weight;

    public Edge(Node from, Node to, int weight) {
        this.from = from;
        this.to = to;
        this.weight = weight;
    }
}
  1. 应用场景
    给的是数组结构,来表达图的存储形式
    在这里插入图片描述
public class CreateGraph {
    public Graph createGraph(int[][] matrix){
        Graph graph=new Graph();
        for(int i=0;i<matrix.length;i++){
            int weight=matrix[i][0];
            int from=matrix[i][1];
            int to=matrix[i][2];
            if(!graph.nodes.containsKey(from)){
                graph.nodes.put(from,new Node(from));
            }
            if(!graph.nodes.containsKey(to)){
                graph.nodes.put(to,new Node(to));
            }
            Node fromNode=graph.nodes.get(from);
            Node toNode=graph.nodes.get(to);
            Edge edge=new Edge(fromNode,toNode,weight);
            graph.edges.add(edge);

            fromNode.out++;
            fromNode.edges.add(edge);
            fromNode.nexts.add(toNode);

            toNode.in++;
        }
        return graph;
    }
}

二、图的遍历

  1. 图的宽度优先遍历
    图的遍历和树的区别就是图可能有环 -> 去重 ->用set实现
public class WidthTraversal {
    public void traversal(Node node){
        if(node == node)
            return;
        Queue<Node> queue=new LinkedList<>();
        Set<Node> set=new HashSet<>();
        queue.add(node);
        set.add(node);
        while(!queue.isEmpty()){
            Node cur=queue.poll();
//            题目要广搜后具体要做什么去替代sout
            System.out.println(cur.value);
            for (Node next : cur.nexts) {
                if(!set.contains(next)){
                    queue.add(next);
                    set.add(next);
                }
            }
        }
    }
}
  1. 深度优先遍历
    仿照宽度优先遍历的话,对于某个节点的所有next,只盯着一个邻居走,等这条路走结束之后,再看这个节点的其他next,为了实现这个效果,这个节点是要被保留在栈里的
public class DepthTraversal {
    public void traversal(Node node ){
        Stack<Node> stack=new Stack<>();
        Set<Node> set=new HashSet<>();
        stack.add(node);
        set.add(node);
        System.out.println(node.value);
        while(!stack.empty()){
            Node cur=stack.pop();
            for (Node next : cur.nexts) {
                if(!set.contains(next)){
                    System.out.println(next.value);
                    set.add(next);
                    stack.add(node);
                    stack.add(next);
                    break;
                }
            }
        }
    }
}

三、图的应用

1、拓扑结构

针对拓扑排序而言:
1)获得入度为0的节点 =》用队列实现
2)当前节点被删掉之后,其邻居节点的入度要修改 =》用map实现,将对应key的值减小
之前我会想着每个去遍历寻找修改之后入读为0的点,但是如果将入度存储下来,不就省去了很多麻烦

public class Topologicalsort {
    public List<Node> sort(Graph graph){
        Map<Node,Integer> map=new HashMap<>();
        Queue<Node> zeroInQueue=new LinkedList<>();
        for (Node node : graph.nodes.values()) {
            map.put(node,node.in);
            if(node.in == 0)
                zeroInQueue.add(node);
        }
        List<Node> list=new ArrayList<>();
        Node cur = zeroInQueue.poll();
        while(!zeroInQueue.isEmpty()){
            list.add(cur);
            for (Node next : cur.nexts) {
// 这边容易出错,可能只讲next的in--了,但是map里next的value没有变化
                map.put(next,map.get(next)-1);
                if(map.get(next) == 0)
                    zeroInQueue.add(next);
            }
        }
        return list;
    }
}

2、最小生成树(K和P)
无论是哪种算法,都要判断加了边(点)之后有没有形成环,用集合机制实现,不在同一个集合里的可以添加,之后将两个分别所属的集合做合并操作(并查集实现是常数级别的)
在这里插入图片描述
1)不直接用并查集实现判环、和并(模仿并查集写)

public class MyUnionFind {
    Map<Node, List<Node>> setMap =new HashMap();
    public MyUnionFind(List<Node> nodes){
        for (Node node : nodes) {
            ArrayList<Node> list=new ArrayList<>();
            list.add(node);
            setMap.put(node,list);
        }
    }
    public boolean isSameSet(Node from,Node to){
        return setMap.get(from) == setMap.get(to);
    }
    public void unionSet(Node from,Node to){
        List<Node> fromSet = setMap.get(from);
        List<Node> toSet = setMap.get(to);
        for (Node node : toSet) {
            fromSet.add(node);
            setMap.put(node,fromSet);
        }
    }
}

2)边的值从小到大输出
用优先级队列PriorityQueue实现,形参为升序比较器(继承Comparator接口)
比较器中compare方法返回的是负数就是升序排序,正数就是降序排序

3)K算法(边)

public class K {
    class EdgeComparator implements Comparator<Edge>{
        @Override
        public int compare(Edge o1, Edge o2) {
            return o1.weight-o2.weight;
        }
    }
    public Set<Edge> kruscal(Graph graph){
        Set<Edge> set=new HashSet<>();
        PriorityQueue<Edge> priorityQueue=new PriorityQueue<>(new EdgeComparator());
        MyUnionFind myUnionFind=new MyUnionFind(new LinkedList<>(graph.nodes.values()));
        for (Edge edge : graph.edges) {
            priorityQueue.add(edge);
        }
        while (!priorityQueue.isEmpty()){
            Edge cur=priorityQueue.poll();
            Node from = cur.from;
            Node to = cur.to;
            if(!myUnionFind.isSameSet(from,to)){
                set.add(cur);
                myUnionFind.unionSet(from,to);
            }
        }
        return set;
    }
}

4)P算法
P算法每次加进来的是个节点,只需对每个加进来的节点和以前的集合来判环,不会像K算法会出现加边导致两个集合连接的要处理两个集合是否可能组成环的事,所有P算法只需要set处理

public class P {
    class EdgeComparator implements Comparator<Edge> {
        @Override
        public int compare(Edge o1, Edge o2) {
            return o1.weight-o2.weight;
        }
    }
    public Set<Node> prim(Graph graph){
        Set<Node> result=new HashSet<>();
//        Set<Node> set=new HashSet<>();
        PriorityQueue<Edge> priorityQueue=new PriorityQueue<>(new EdgeComparator());
        for (Node node : graph.nodes.values()) {
            if(!result.contains(node)){
                result.add(node);
                for (Edge edge : node.edges) {
                    priorityQueue.add(edge);
                }
                while (!priorityQueue.isEmpty()){
                    Edge edge = priorityQueue.poll();
                    if(!result.contains(edge.to)){
                        result.add(edge.to);
                        for (Edge edge1 : edge.to.edges) {
                            priorityQueue.add(edge1);
                        }
                    }
                }
            }
        }
        return result;
    }
}
  1. 最短距离
    1)Dijkstra
    给定一个点,要求这个点到其余所有点的最短距离
public class Dijkstra {
    public HashMap<Node,Integer> dijkstra(Node head){
        HashMap<Node,Integer> distanceMap=new HashMap<>();
        HashSet<Node> selectedNodes=new HashSet<>();
        distanceMap.put(head,0);
        Node curNode = getMinDistanceAndUnselectedNode(distanceMap, selectedNodes);
        while(curNode != null){
            int curDistance=distanceMap.get(curNode);
            for (Edge edge : curNode.edges) {
                Node to = edge.to;
                if(!distanceMap.containsKey(to)){
                    distanceMap.put(to,curDistance+edge.weight);
                }
                distanceMap.put(to,Math.min(curDistance+edge.weight,distanceMap.get(to)));
            }
            selectedNodes.add(curNode);
            curNode=getMinDistanceAndUnselectedNode(distanceMap,selectedNodes);
        }
        return distanceMap;
    }
    public Node getMinDistanceAndUnselectedNode(HashMap<Node,Integer> distanceMap,HashSet<Node> selectedNodes){
        int minDistance=Integer.MIN_VALUE;
        Node node=null;
        for (Map.Entry<Node, Integer> entry : distanceMap.entrySet()) {
            if(!selectedNodes.contains(entry.getKey()) && entry.getValue()<minDistance){
                minDistance=entry.getValue();
                node=entry.getKey();
            }
        }
        return node;
    }
}

在这里插入图片描述

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值