JAVA-图

(如代码有误,烦请各位指正)

1.图的结构:用点集加边集的形式来实现

//图定义包含两部分:点集和边集
class Graph{
    HashMap<Integer,Node> nodes;
    HashSet<Edge> edges;
    public Graph(){
        nodes=new HashMap<Integer, Node>();
        edges=new HashSet<Edge>();
    }
}
//点集:该点的值;进入该点的边数;从该点出去的边数;从该点出去相连的点的集合;从该点出去的边的集合
class Node{
    int value;
    int in;
    int out;
    ArrayList<Node> nexts;
    ArrayList<Edge> edges;
    public Node(){
        in=0;
        out=0;
        nexts=new ArrayList<>();
        edges=new ArrayList<>();
    }
}
//边集:该边的权重;与该边相连的两个点
class Edge{
    int weight;
    Node from;
    Node to;
    public Edge(int weight,Node from,Node to){
        this.weight=weight;
        this.from=from;
        this.to=to;
    }
}

2.图的宽度优先遍历(Breadth First Search)

图的宽度优先遍历跟二叉树的优先遍历类似,不过要防止图中的环使程序无法停止的情况。
解决方法:增加一个集合,用来记录已经遍历过了哪些点,保证不发生一个点被重复遍历的情况。

    public static void GraphBFS(Node head){
        if(head==null){
            return;
        }
        HashSet<Node> set=new HashSet<>();
        Queue<Node> queue=new LinkedList<>();
        set.add(head);
        queue.add(head);
        while(!queue.isEmpty()){
            Node cur=queue.poll();
            System.out.println(cur.value);
            for(Node next: cur.nexts){
                if(!set.contains(next)){
                    set.add(next);
                    queue.add(next);
                }
            }
        }
    }

3.图的深度优先遍历(Deepth First Search)

    //图的深度优先遍历
    public static void GraphDFS(Node head){
        if(head==null){
            return;
        }
        Stack<Node> stack=new Stack<>();
        HashSet<Node> set=new HashSet<>();
        stack.add(head);
        set.add(head);
        System.out.println(head.value);//压入栈即打印
        while(!stack.isEmpty()){
            Node cur=stack.pop();
            for(Node next:cur.nexts){
                if(!set.contains(next)){
                    stack.add(cur);//cur还有其他nexts点没被输出,等待下一次,需要压入栈
                    stack.add(next);
                    set.add(next);
                    System.out.println(next.value);//往下找到一个点就打印
                    break;//深度优先遍历,每次只往下取一个点
                }
            }
        }
    }

4.拓扑排序(有向无环图)

一个较大的工程往往被划分成许多子工程,我们把这些子工程称作活动(activity)。在整个工程中,有些子工程(活动)必须在其它有关子工程完成之后才能开始,也就是说,一个子工程的开始是以它的所有前序子工程的结束为先决条件的,但有些子工程没有先决条件,可以安排在任何时间开始。为了形象地反映出整个工程中各个子工程(活动)之间的先后关系,可用一个有向图来表示,图中的顶点代表活动(子工程),图中的有向边代表活动的先后关系,即有向边的起点的活动是终点活动的前序活动,只有当起点活动完成之后,其终点活动才能进行。通常,我们把这种顶点表示活动、边表示活动间先后关系的有向图称做顶点活动网(Activity On Vertex network),简称AOV网。拓扑排序
思路:先找入度为0的点,输出,把该点有关的影响全部删掉,再找入度为0的点,重复此过程。

    //拓扑排序
    public static List<Node> sortedTopology(Graph graph){
        //记录图各个点的入度是多少
        HashMap<Node,Integer> inMap=new HashMap<>();
        //入度为0的点进入队列(使用队列的先进先出)
        Queue<Node> zeroInQueue=new LinkedList<>();
        //遍历图中的节点,找到入度为0的节点进入队列,确定每个节点的入度并更新哈希表
        for (Node cur:graph.nodes.values()){
            inMap.put(cur, cur.in);
            if(cur.in==0){
                zeroInQueue.add(cur);
            }
        }
        ArrayList<Node> result=new ArrayList<>();
        while(!zeroInQueue.isEmpty()){
            Node cur=zeroInQueue.poll();
            result.add(cur);//将弹出的入度为0的节点保留在result中
            for(Node next: cur.nexts){
                inMap.put(next, next.in-1);//消除已弹出节点的影响
                if(inMap.get(next)==0){
                    zeroInQueue.add(next);//队列中加入新的入度为0的节点
                }
            }
        }
        return result;
    }

5.最小生成树(无向图)

(1)k算法*kruskal:从边的角度出发,依次选择最小的边,看有没有形成环,没形成环则加上,形成则不加。
难点:怎么确定能不能形成环:集合查询合并(假定所有的点一开始是一个单独的集合,加一条边,则边连的点聚合成一个集合,若边的点在同一个集合里,则不要该条边)

代码后续补

(2)p算法(prim):从点的角度出发,从被解锁的边中选一条权重最小的边,加入相连的点,再根据这个点,解锁相连的边,从所有被解锁的边中选权重最小且之前没有被选择的一条边(这条边要拽进来一个新的点),重复上述步骤。

    //p算法
    //implements用来实现接口
    public static class EdgeCompartor implements Comparator<Edge>{
        @Override//加上Override标签后如果你写错重写的方法,系统会给你检验
        public int compare(Edge e1, Edge e2){
            return e1.weight-e2.weight;
        }
    }
    public static Set<Edge> primMST(Graph graph){
        //小根堆存储已经被解锁的边,用来排序,方便弹出最短的边(用自己规定的比较方法)
        PriorityQueue<Edge> priorityQueue=new PriorityQueue<>(new EdgeCompartor());
        //用来记录已经被解锁的点
        HashSet<Node> nodeSet=new HashSet<>();
        //用来存储被选择的边
        HashSet<Edge> result=new HashSet<>();
        //循环是为了解决图的点存在不连通的情况
        for(Node node:graph.nodes.values()){
            if(!nodeSet.contains(node)){
                nodeSet.add(node);
                for(Edge edge:node.edges){
                    priorityQueue.add(edge);//解锁该点相关的边,会有重复的边进入,但是有nodeSet
                }
                while(!priorityQueue.isEmpty()){
                    Edge curEdge=priorityQueue.poll();//弹出最小的一个边
                    Node toNode=curEdge.to;
                    if(!nodeSet.contains(toNode)){
                        result.add(curEdge);//确定该条边可以解锁一个新的点
                        nodeSet.add(toNode);//解锁新的点要加入到nodeSet中
                        for(Edge edge:toNode.edges){
                            priorityQueue.add(edge);//解锁该点相关的边,会有重复的边进入,但是有nodeSet
                        }
                    }
                }

            }
        }
        return result;
    }

6.Dijkstra算法(没有权值和为负的环,一定要规定出发点)

从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。Dijkstra
思路:首先初始化一张表,记录出发点到所有点的初始距离;看出发点到所有点的距离,当某一个点的距离变小时更新表。

代码后续补
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值