图相关(附相关算法JAVA源码)

一、相关定义

图(Graph)

  • 由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
  • 图中的数据元素称为顶点(Vertex)。
  • V是有穷非空集合,不能是空集。
  • 顶点之间的逻辑关系用边来表示,边集可以是空的。

无向图(Undirected graphs)

  • 无向边(Edge): 若两个顶点之间的边没有方向,则称这条边为无向边;
  • 若图中任意一条边都是无向边,则称图为无向图;
  • 在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图。
  • 包含n个顶点的无向完全图有n*(n-1)/ 2条边。

有向图(Directed graph)

  • 有向边:若从一个顶点到另一个顶点有方向,则称这条边为有向边,也称为弧(Arc)
  • 若图中任意一条边都是有向边,则称图为有向图;
  • 在有向图中,如果任意两个顶点之间都存在方向互为相反的两条弧,则称该图为有向完全图。
  • 包含n个顶点的有向完全图有n*(n-1)条边。

网(Network)

  • 有些图的边或弧有与它相关的数字,叫做权(weight);
  • 带权的图通常称为网。

连通图(Connected Graph)

  • 在无向图中,若两个顶点之间有路径,则称两个点是连通的;
  • 若任意两个顶点都是连通的,则称为连通图。
  • 无向图中的极大连通子图称为连通分量;

强连通图

  • 在有向图中,若每一对顶点v~i~、v~j~,从v~i~到v~j~都存在路径,则称图为强连通图;
  • 极大强连通子图称为强连通分量。

生成树

  • 连通图的生成树是一个极小的连通子图,它包含图中的全部顶点,却只有n-1条边;
  • 若有向图只有一个顶点入度为0,其余均为1,则是一棵有向树(点的入度是以该点为头的弧的数目,即方向指向它的弧,反之为出度);
  • 一个有向图的生成森林由若干棵有向树组成,含有图中全部顶点,但只有足以构成若干棵不相交的有向树的弧。

二、图的存储结构

邻接矩阵(Adjacency Matrix)

用两个数组来表示图,一个一维数组表示顶点,一个二维数组(邻接矩阵)存储图中边或弧的信息。

  • 在无向图邻接矩阵中,横纵坐标是n个顶点,若对应的两个订点之间有一条边,记为1,其余记为0,必为对称矩阵;
  • 在有向图中,每一行为以对应点为起点的边,有为1无为0,所以不是对称矩阵;
  • 在网图中,若两点之间有边,记录对应的权重,若两点相等,记为0,若无边,记为∞。
矩阵特点:
  • 可以轻易判定两定点之间是否有边(1有0无);
  • 一个顶点的度(一个顶点的边数)为对应的行或列元素之和(在有向图中,入度为列各元素之和,出度为行各元素之和);
  • 求顶点邻接点即为把该点对应的行或列扫描一遍。
邻接矩阵存储结构:
 private static class Matrix{
        String[] vexs;        //顶点数组
        Integer[][] arc;      //邻接矩阵
        int verNum,edgeNum;  //顶点数和边数
    }
创建过程代码:
public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        System.out.println("请输入顶点数:");

        matrix.verNum = in.nextInt();

        System.out.println("请输入边数:");
        matrix.edgeNum = in.nextInt();

        matrix.vexs = new String[MAXVEX];
        matrix.arc = new Integer[MAXVEX][MAXVEX];

        System.out.println("请输入每个顶点:");
        for (int i = 0; i < matrix.verNum; i++) {
            matrix.vexs[i] = in.next();
        }

        for (int i = 0; i < matrix.verNum; i++) {
            for (int j = 0; j < matrix.verNum; j++) {
                matrix.arc[i][j] = INFINITY;
            }
        }

        for (int k = 0; k < matrix.edgeNum; k++) {
            System.out.println("请输入边上的下标i,j和权重w:");
            int i = in.nextInt();
            int j = in.nextInt();
            int w = in.nextInt();
            matrix.arc[i][j] = w;
        }

        for (int i = 0; i < matrix.verNum; i++) {
            Integer[] a = matrix.arc[i];
            for (int j = 0; j < matrix.verNum; j++) {
                if(i == j)
                    System.out.print(0+",  ");
                else if(a[j] == INFINITY)
                    System.out.print('\u221E'+", ");
                else
                    System.out.print(a[j] + ",  ");
            }

            System.out.println();
        }
    }

输出结果:

请输入顶点数:
5
请输入边数:
6
请输入每个顶点:
v0 v1 v2 v3 v4
请输入边上的下标i,j和权重w:
1 0 9
请输入边上的下标i,j和权重w:
1 2 3
请输入边上的下标i,j和权重w:
2 0 2
请输入边上的下标i,j和权重w:
2 3 5
请输入边上的下标i,j和权重w:
3 4 1
请输入边上的下标i,j和权重w:
0 4 6
0,  ∞, ∞, ∞, 6,  
9,  0,  3,  ∞, ∞, 
2,  ∞, 0,  5,  ∞, 
∞, ∞, ∞, 0,  1,  
∞, ∞, ∞, ∞, 0,  

邻接表(Adjacency List)

  • 一维数组存放顶点;
  • 对无向表,每个顶点作为一个单链表的头指针依次连接与它相邻的点。
  • 对有向表,以顶点的弧尾存储边表;
  • 对网图,为链表节点增加一个权重的数据域。

一个邻接表结构如下:

//边表结点
    private static class EdgeNode{
        int adjvex;            //邻接点域
        int weight;            //网图的权重
        EdgeNode next;          //next结点
    }

    //顶点表结点
    private static class VertexNode{
        String data;           //顶点名称
        EdgeNode firstEdge;    //边表头指针
    }

    //邻接表
    private static class AdjacencyGraph{
        VertexNode[] vertexNodes;       //存放顶点的数组
        int VertexNum,EdgeNum;         //顶点数和边数
    }

十字链表(Orthogonal List)

逆邻接表:

有向图调换箭头方向后的邻接表

十字链表:

结合邻接表和逆邻接表,在邻接表中加入入边,以方便查找。

结点代码如下:

//边表结点
    private static class EdgeNode{
        int tailvex,headvex;            //邻接点域
        int weight;            //网图的权重
        EdgeNode headlink,taillink;          //出入结点
    }

    //顶点表结点
    private static class VertexNode{
        String data;           //顶点名称
        EdgeNode firstin;    //边表头入指针
        EdgeNode firstOut;   //边表头出指针

    }

    //十字链表
    private static class AdjacencyGraph{
        VertexNode[] vertexNodes;       //存放顶点的数组
        int VertexNum,EdgeNum;         //顶点数和边数
    }

邻接多重表

在无向图中,使用邻接表,每一条边会重复两次,在删除时会造成不便,因此采用邻接多重表。

邻接多重表有两部分组成:
- 顶点:由顶点组成的数组;
- 边表结点:一个结点中包含四个域:边依附的两个顶点、每个顶点的另一条边。

结点代码如下:

    //边表结点
    private static class EdgeNode{
        int ivex,jvex;            //邻接点域
        EdgeNode ilink,jlink;          //next结点
        boolean mark;              //标记是否被搜索过
    }

    //顶点表结点
    private static class VertexNode{
        String data;           //顶点名称
        EdgeNode firstEdge;    //边表头指针

        VertexNode(String s){
            this.data = s;
            this.firstEdge = null;
        }

    }

    //邻接表
    private static class MulipleGraph{
        VertexNode[] vertexNodes;       //存放顶点的数组
        int VertexNum,EdgeNum;         //顶点数和边数
    }

创建方法关键代码:

 for (int k = 0; k < multilistGraph.EdgeNum; k++) {
            System.out.println("请输入顶点序号i,j:");
            int i = in.nextInt();
            int j = in.nextInt();
            EdgeNode edgeNode = new EdgeNode();
            edgeNode.ivex = i;
            edgeNode.jvex = j;
            edgeNode.ilink = multilistGraph.vertexNodes[i].firstEdge;
            edgeNode.jlink = multilistGraph.vertexNodes[j].firstEdge;
            multilistGraph.vertexNodes[i].firstEdge = edgeNode;
            multilistGraph.vertexNodes[j].firstEdge = edgeNode;
        }

边集数组

边集数组是由两个一维数组构成,一个存储顶点的信息,另一个存储边的信息,这个边数组每个数据元素由一条边的起点下标、终点下标和权组成。

边集数组的结构:
class Edge{
    int begin;     //起点下标
    int end;       //终点下标
    int weight;    //权重
}

在边集数组中需要扫描整个边数组,效率不高。

图的遍历

附上链接:DFS和BFS

最小生成树算法

附上链接: 最小生成树算法

最短路径

附上链接: 最短路径算法

拓扑排序与关键路径

附上链接:拓扑排序与关键路径

本文用到的代码链接

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值