Java中图的通用数据结构实现、接口函数、宽度优先和深度优先遍历

图的分类

无向图与有向图
有向图:图中的边存在方向性
无向图:边无方向性
非加权图与加权图
加权图:图中的每一条边都一个实数预置对应,即每条边都对应于一个权重
非加权图:默认为每条边的权重都是一致的
连通图与非连通图
连通图:图中每个顶点都可达
非连通图:图中存在孤立的点,即没有任何边与之连接

Java中通用描述图的数据结构

下面的代码构建的图是有向图,加权图,非连通图(可能存在孤立的点)

点类 Node

如果存在孤立的点,它的入读和出度都为零

package com.ambitfly.arithmetic.graph;

import java.util.ArrayList;

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;
        in = 0;
        out = 0;
        nexts = new ArrayList<>();
        edges = new ArrayList<>();
    }
}

边类 Edge

package com.ambitfly.arithmetic.graph;

public class Edge {
    public int weight;  //A到B的权重(权值)
    public Node from;  
    public Node to;   //从from到to

    public Edge(int weight, Node from, Node to) {
        this.weight = weight;
        this.from = from;
        this.to = to;
    }
}

图类

package com.ambitfly.arithmetic.graph;

import java.util.HashMap;
import java.util.HashSet;

public class Graph {
    public HashMap<Integer,Node> nodes;  //点集
    public HashSet<Edge> edges;  //边集

    public Graph() {
        nodes = new HashMap<>();
        edges = new HashSet<>();
    }
}

图的转换接口函数

​ 由于图的表达形式多种多样,如果没有一个统一的数据结构描述图,那么解题的时候就要对每一种图的数据结构实现一套对应的算法。所以要构建一套通用的结构函数,把多种多样的图转化为上面图的数据结构,每次都使用上面的数据结构解题即可。

/**
     * 图的转换接口,使用一套代码实现图的所有算法。
     * @param matrix
     * @return
     */
    public static Graph createGraph(Integer[][] matrix) {
        Graph graph = new Graph();
        for (int i = 0; i < matrix.length; i++) {
            Integer from = matrix[i][0];  // from点
            Integer to = matrix[i][1];    // to点
            Integer weight = matrix[i][2];// 权值

            // 如果from点不存在于图中添加
            if (!graph.nodes.containsKey(from)) {
                graph.nodes.put(from, new Node(from));
            }
            
			// 如果to点不存在于图中添加
            if (!graph.nodes.containsKey(to)) {
                graph.nodes.put(to, new Node(to));
            }
			
           	//构建from->to这条边
            Node nodeFrom = graph.nodes.get(from);
            Node nodeTo = graph.nodes.get(to);
            Edge edge = new Edge(weight, nodeFrom, nodeTo);
			
            //添加from点的nexts(发散出去的点)添加to点
            nodeFrom.nexts.add(nodeTo);
            //给from点的出度加1
            nodeFrom.out++;
            //给to点的入度加1
            nodeTo.in++;
            //给from点的edges(发散出去的边)添加边
            nodeFrom.edges.add(edge);
            //给图的边集添加边
            graph.edges.add(edge);
        }
        
        return graph;
    }

图的宽度优先遍历

​ 图的宽度优先遍历(BFS)算法是一个分层搜索的过程,和树的层序遍历算法相同。在图中选中一个节点,作为起始节点,然后按照层次遍历的方式,一层一层地进行访问。

package com.ambitfly.arithmetic.graph;


import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;

/**
 * 图的宽度优先遍历
 */
public class Code_BFS {

    /**
     *
     * @param node 从一个点出发
     */
    public static void bfs(Node node) {
        if (node == null) {
            return;
        }

        Queue<Node> queue = new LinkedList<>();
        HashSet<Node> set = new HashSet<>();  //set保证了被访问的节点不会被再次访问

        queue.add(node);
        set.add(node);

        // 类似于树的宽度优先遍历(多了一个Set结构确定图节点是否被访问过)
        while (!queue.isEmpty()) {
            Node cur = queue.poll();
            System.out.println(cur.value);
            for (Node next : cur.nexts) {
                if (!set.contains(next)) {
                    queue.add(next);
                    set.add(next);
                }
            }
        }
    }
}

图的深度优先遍历

​ 深度优先遍历(DFS)就是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。

深度优先遍历图的方法是,从图中某顶点v出发:
(1)访问顶点v;
(2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
(3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止

package com.ambitfly.arithmetic.graph;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Stack;
import java.util.logging.Handler;

/**
 * 图的深度优先遍历
 */
public class Code_DFS {
    public static void dfs(Node node){
        if (node == null) {
            return;
        }
        Stack<Node> stack = new Stack<>();
        HashSet<Node> set = new HashSet<>();

        stack.add(node);
        set.add(node);
        System.out.println(node.value);

        /**
         * A -> 打印
         * A -> pop
         * A -> add
         * A.next(first) -> add
         * 周而复始
         */
        while (!stack.isEmpty()) {
            Node cur = stack.pop();

            for (Node next : cur.nexts) {
                if (!set.contains(next)) {
                    stack.add(cur);
                    stack.add(next);
                    set.add(next);
                    System.out.println(next.value);
                    break;
                }
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值