# 快速看懂链式向前星(JAVA实现)构建图

Posted tacit-lxs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了# 快速看懂链式向前星(JAVA实现)构建图相关的知识,希望对你有一定的参考价值。

快速看懂链式向前星(JAVA实现)

1.概念

它是一种存储图数据的一种方法,使用它可以轻易完成图的构建。相比于邻接矩阵更容易实现,只是不如邻接矩阵灵活。它和邻接表类似,只不过它是用的头插法,每个点形成的链条顺序和邻接表相反。

  • 它由边集数组edges[]和头结点head[]数组构成

    edges[i]表示数组中存放的第 i 条边,head[i]表示以i为起点的第一条边的下标,比如2这个点,以他为起点有两条边,假设边输入顺序为1->2(第0条边)、2->3(第1条边)、2->4(第2条边)。 那么head[2] = 2,以2
    为起点的第一条边在数组edges中的下标为2。

边集数组

更具体的,如上面这张有向带权图(对于无向图可以看作两个结点之间有正向和反向的有向图),我们知道每个结点的编号1、2、3、4,以及每一条边的权重。

首先给每一条边编号(从0开始),这里假设1->2为第0条边,2->3为第1条边,2->4为第2条边。

结点信息用一个类表示

    //    建立边集
    public static class Edge
        int to;
        int w;
        int next;
    

这样访问第i条边的去向即为:edges[i].to,访问第i条边的权重即为edges[i].w

头结点

你肯定在疑惑next表示什么,并且会为怎么访问一个点的邻接点、遍历这个图而困惑,next就是answer。

首先得从添加每一条边的方式讲起。

public static void add(int u,int v,int w)
    edges[cnt] = new Edge();//创建一条边将其放入边集数组
    edges[cnt].to = v;
    edges[cnt].w = w;
    edges[cnt].next = head[u];
    head[u] = cnt++;

其中cnt是一个全局变量,初始化为0,表示每一条边的下标。

然后就是我们的主角head头结点数组

head = new int[node_num + 1];//这里结点数加1,是因为结点编号从1开始的。
for (int i = 0; i < head.length; i++) 
	head[i] = -1;

现在开始整个构建过程

  • add(1,2,3)加载第一条边。

    在调用这个方法是,cnt = 0;所以edges[0].to = 2(表示结点1去往结点2), edges[0].w = 3(表示第0条边的权重为3)。

    下面是重点,edges[0].next = head[1] = -1(表示以节点1为起点的下一条边的位置,因为结点1为起点的只有一条边,所以-1就代表它没有下一条边了)。

    最后,head[1] = cnt++, 这里表示head[1] = 0, cnt = 1;

    结果如图:

  • add(2,3,4)

    重复上面的流程,得到

  • add(2,3,5)

    继续重复得到:


到此建图完毕,我们可以根据head数组,得到每条边在edges数组中的位置,然后访问edges数组得到每一条边的信息。
比如,head[2] = 2, 说明以2为起点第一条边在edges数组中的下标为2,说明这是第2条边,然后根据下标就可以访问edges数组,得到对应边的信息。从上图看出deges[2].next 为1,表示以2为起点的下一条边的下标为1,再得到其信息,这样就可以遍历以2为起点的所有边。当我们依次从每个结点这样访问一边,就遍历了整个图。
特别的
对于无向图,我们可以用异或操作轻松的获取一条边的反向边。
比如有0,1,2,3,4,5条边,0和1互为反向边,3和4互为反向边,4和5互为反向边。那么 当我们想知道第i条边的反向边是那条边时,就可用 i异或1得到。具体的,4^1 = 5 ,5^1 = 4

public static void bfs(int node_num)
//  标记数组,用于标记没有访问过的结点    
    boolean[] visited = new boolean[node_num+1];
    Queue<Integer> queue = new LinkedList<>();
    queue.offer(1);
    while (!queue.isEmpty())
        int u = queue.poll();
        System.out.print(u+" ");
//        从某个点开始,访问这个点出发的所有边
        for (int i = head[u]; i != -1; i = edges[i].next) 
            int f = edges[i].to;
            if (!visited[f])
                queue.offer(f);
                visited[f] = true;
            
        
    

2.代码

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class ChainForwardStar 

    private static Edge[] edges ;//边集数组,存放所有的边
    private static int cnt = 0; //记录边的下标,比如,如果边的编号从零开始,那么第1条边的下标就是0
    private static int[] head;//头结点数组,

    //    建立边集
    public static class Edge
        int to;
        int w;
        int next;
    

    public static void main(String[] args) 
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入节点个数:");
        int node_num = scanner.nextInt();
        head = new int[node_num + 1];
        for (int i = 0; i < head.length; i++) 
            head[i] = -1;
        
        System.out.println("请输入边的数量:");
        int edge_num = scanner.nextInt();
        edges = new Edge[edge_num];
//        scanner.nextLine();
        System.out.println("请输入所有边,及他们的权重:");
//        建图
        for (int i = 0; i < edge_num; i++) 
//            String str = scanner.nextLine();
//            String[] split = str.split(" ");
//            int u = Integer.valueOf(split[0]);
//            int v = Integer.valueOf(split[0]);
//            int w = Integer.valueOf(split[0]);
            int u = scanner.nextInt();
            int v = scanner.nextInt();
            int w = scanner.nextInt();
            add(u,v,w);
        
//        遍历某个节点的邻接点
        System.out.println("请输入想要遍历的结点:");
        int node = scanner.nextInt();
        for (int u = head[node]; u != -1; u = edges[u].next) 
            System.out.println("第"+u+"条边的去往结点-》"+ edges[u].to);
            System.out.println("第"+u+"条边的的权重" + edges[u].w);
            if (u == -1)
                break;
            
            System.out.println(node + "号结点上出发的下一条边的下标:"+edges[u].next);
        
//        输出访问结点的顺序
        bfs(node_num);
    

    public static void add(int u,int v,int w)
        edges[cnt] = new Edge();//创建一条边将其放入边集数组
        edges[cnt].to = v;
        edges[cnt].w = w;
        edges[cnt].next = head[u];
        head[u] = cnt++;
    
    public static void bfs(int node_num)
        boolean[] visited = new boolean[node_num+1];
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(1);
        while (!queue.isEmpty())
            int u = queue.poll();
            System.out.print(u+" ");
            for (int i = head[u]; i != -1; i = edges[i].next) 
                int f = edges[i].to;
                if (!visited[f])
                    queue.offer(f);
                    visited[f] = true;
                
            
        
    


3. 结果

以上是关于# 快速看懂链式向前星(JAVA实现)构建图的主要内容,如果未能解决你的问题,请参考以下文章

# 快速看懂链式向前星(JAVA实现)构建图

链式向前星知识点&代码

链式向前星 - 学习理解

[转]图论-链式向前星

POJ #1789 Truck History 最小生成树(MST) prim 稠密图 链式向前星

链式前向星