学习图相关的算法(Java 实现)(1)

学习图相关的算法(Java 实现)(1)


学习图相关的算法(Java 实现)(2)——Prim算法求最小生成树

前言

最近做题的时候发现图相关的算法都忘光了根本就没有学会过,于是知耻而后勇,决定重新系统学习一下,争取在一个月一季度完成吧

学习资料:
1.《算法(第四版)》
2.Hyperskill中图相关的部分


相关知识点

1.形式定义

图(Graph) 由两部分组成: G = (V, E)
其中 V 表示所有 节点(node)集合(Set)
E 表示所有 边(Edge)集合(Set), 其中的每一条边有 2个 连接的节点

举例1:
在这里插入图片描述
G = (V, E)
V = {0, 1, 2, 3, 4}
E = {{1, 2}, {1, 3}, {2, 4}}

举例2:
在这里插入图片描述
G = (V, E)
V = {A, B, C}
E = ∅


2.简单图与复杂图

在这里插入图片描述

在上图中
边{0, 0}连接同一个结点,类似这种边称为 自环边(self-loop)
连接结点0和结点1有三条边{0, 1},这些边称为 平行边(parallel edges)
存在自环边和/或平行边的图称为复杂图(multigraph)
其它的图称为 简单图(simple graph)


3.边和节点的关系

在这里插入图片描述
如果2个节点通过1条边直接连接,那么称它们为 相邻(adjacent) 节点。如上图中节点1和节点2是相邻的,而节点1和节点4不相邻。
1条边连接的2个节点 关联(incident) 这条边,如上图中节点1和节点3关联边{1, 3},节点1还关联边{1, 2}
1个节点关联的边的数量叫做这个节点的 度(Degree)。如上图中节点1的度为2,节点3的度为1,节点0的度为0


4.有向图

如果对于图中的边不是双向的,而是有起点和终点,这样的图叫做 有向图(directed graphs) 否则叫做 无向图(undirected graphs)
如下面的有向图:
在这里插入图片描述

在有向图中,每个节点的度为两部分 入度(indegree)出度(outdegree) 之和,入度是指指向该节点的边的数量,出度是指从该节点出发的边的数量
在上图中,节点1的出度是2,入度是0;节点2的入度是1,出度是1;节点3的入度是2,出度是0;节点4的入度是1,出度是1。


5.路径和环

路径(Path) 是图中有序且连续的一组点和边,在图中如果一个路径中没有重复的节点,即一个节点在该路径中最多出现一次,这个路径就是简单的,称它为简单路径(simple path)
在这里插入图片描述
上图中,蓝色的边和与其关联的节点组成一条路径1->2->4->3->5,你能数清上图中从节点1到节点5有多少条简单路径吗?
在这里插入图片描述

如果一条路径的出发节点和结束节点为同一个节点,这个路径就是一个环(cycle),如上图中1->2->3->4->1就是一个环。


6.图的连通性

在无向图中,如果节点x和节点y之间存在至少一条路径,那么节点x和节点y就是 联通(connected) 的,如果在一个节点的集合中,任意两个点都是联通的,那么这个集合就是一个 联通分量(connected component)
在这里插入图片描述
在上面的图中,显然有2个联通分量,一个是{0},一个是{1, 2, 3, 4},如果一个图只有一个联通分量,那么这个图就是联通图(connected graph) ,否则这个图就不联通。


7.子图

将一个图移除任意数量的边和/或节点所得到的图叫做这个图的子图(subgraph),如下面的例子
在这里插入图片描述
上图中图2是图1的子图,图3是图2的子图,图3还是图1的子图。


8.图的存储方式

图有两种主要的存储方式:邻接矩阵(adjacency matrix)邻接表(adjacency lists)
还有一些其它的存储方式,比如用集合存储图中所有的边。。。

(1).邻接矩阵

直接用一个矩阵来表示两个节点是否相邻
在这里插入图片描述
在这里插入图片描述

在上面的矩阵中,矩阵值为1代表两节点相邻,

如果是边比较密集的图,用邻接矩阵来存储会更好些。

(2).邻接表

用邻接表来存储每个节点的相邻节点
在这里插入图片描述
在这里插入图片描述

如果想要查找图的两个节点是否相邻,那么邻接表并不是一个好的选择。

邻接矩阵和邻接表的复杂度

-----空间复杂度添加边时间复杂度检查两个节点是否相邻时间复杂度遍历一个节点相邻的所有节点时间复杂度
邻接矩阵O(V2)O(1)O(1)O(V)
邻接表O(E+V)O(1)degree(v)degree(v)

MyGraph类(无向图,邻接表实现)

import java.util.*;
/**
 * 无向图类
 *
 */
public class MyGraph {

    private final int V; //顶点数目
    private int E; //边的数目
    private ArrayList<Integer>[] adjacency; //邻接表存储边
    /**
     * 构造一个有v个节点的图
     * @param v 图中节点的数量
     */
    public MyGraph(int v) {
        V = v;
        adjacency = new ArrayList[V];
        for(int i = 0; i < V; ++i) {
            adjacency[i] = new ArrayList<>();
        }
    }
    /**
     * 获取图中节点的数量
     * @return 图中节点的数量
     */
    public int V() {
        return V;
    }
    /**
     * 获取图中边的数量
     * @return 图中边的数量
     */
    public int E() {
        return E;
    }
    /**
     * 向图中添加一条边
     * @param v 与边相连的节点1的序号
     * @param w 与边相连的节点2的序号
     */
    public void addEdge(int v, int w) {
    	adjacency[v].add(w);
    	adjacency[w].add(v);
        E++;
    }
    /**
     * 获取指定节点的邻接表
     * @param v 节点的序号
     * @return 一个节点相邻的节点的邻接表
     */
    public ArrayList<Integer> adjacency(int v) {
    	return adjacency[v];
    }
}

那么如果节点的值不是int类型的呢?
可以将MyGraph类封装成泛型类
那么需要有一个泛型的数组存储每个节点的值,同时还需要一个哈希表存储值到节点索引

import java.util.*;

/**
 * 泛型图类
 * @param <Object> 图中节点的类型
 */
public class ObjectGraph<Object> {
	private MyGraph graph; //无向图
	private Object[] objects; //存储节点值
	private HashMap<Object, Integer> directory; //节点值到节点索引的哈希表
	/**
	 * 构造方法
	 * @param objects 图中每个节点的值
	 */
    public ObjectGraph(Object[] objects) {
        this.graph = new MyGraph(objects.length);
        this.objects = objects.clone();
        directory = new HashMap<>();
        for(int i = 0; i < objects.length; ++i) {
        	directory.put(objects[i], i);
        }
    }
    /**
     * 获取图中节点的数量
     * @return 图中节点的数量
     */
    public int V() {
        return graph.V();
    }
    /**
     * 获取图中边的数量
     * @return 图中边的数量
     */
    public int E() {
    	return graph.E();
    }
    /**
     * 向图中添加一条边
     * @param v 与边相连的节点1的值
     * @param w 与边相连的节点2的值
     */
    public void addEdge(Object v, Object w) {
        graph.addEdge(directory.get(v), directory.get(w));
    }    
//    public ArrayList<Integer> adjacency(int v) {
//    	return graph.adjacency(v);
//    }
    /**
     * 获取指定节点的邻接表
     * @param v 节点的序号
     * @return 一个节点相邻的节点的邻接表
     */
    public ArrayList<Object> adjacency(int v) {
    	ArrayList<Object> objectAdjacency = new ArrayList<>();
    	Iterable<Integer> adjacency = graph.adjacency(v);
    	for(int index: adjacency) {
    		objectAdjacency.add(objects[index]);
    	}
    	return objectAdjacency;
    }
}

更多内容就下次再更啦~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值