# 快速看懂链式向前星(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. 结果

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tacit-lxs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值