数据结构与算法之邻接表及其实现

138 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第36天,点击查看活动详情

✨欢迎关注🖱点赞🎀收藏⭐留言✒

🔮本文由京与旧铺原创,csdn首发!

😘系列专栏:算法学习

💻首发时间:🎞2022年12月31日🎠

🀄如果觉得博主的文章还不错的话,请三连支持一下博主哦

🎧作者是一个新人,在很多方面还做的不好,欢迎大佬指正,一起学习哦,冲冲冲


邻接表

邻接矩阵是使用顺序表的形式储存顶点之间的关系,而邻接表是通过链表的形式表示顶点间的关系,本质上的逻辑是一样的。

假设图一共有nn个顶点,则邻接表由nn个链表组成,每个结点表示一个顶点,链表中储存该顶点指向了哪一些点。

10 对于带权值的图,也是可以使用邻接表表示的,只不过链表的结点是一个类或者数组,里面储存了顶点和到该顶点的权值大小。 11 下面同理,我们来尝试实现一个邻接表,邻接表是基于链表实现的,链表可以使用数组实现也可以使用结点来实现,一般使用结点进行实现,但是在竞赛当中是基于数组实现的,本文为了与邻接矩阵统一,还是使用结点实现吧。

邻接表是由多个带头的链表实现的,每个链表结点中至少需要next域和数据域,数据域包括顶点和边的权值,所以我们需要设计一个结点,需要包含链表的指针域和顶点和边的权值。

class GNode {
    public GNode next;
    public int weight;
    public String point;

    public GNode(){}
    public GNode(String point, int weight) {
        this.point = point;
        this.weight = weight;
    }
}

下面就是设计邻接表类,我们需要一个顶点数组,一个顶点数组与链表下标的对应关系,也就是一个哈希表,还需要一个是否为有向图的判断标志,初始化的时候就不需要去初始化链表数组了,只需要初始化顶点数组和哈希表对应关系即可。

    //存储顶点
    private String[] points;
    //每个顶点的边
    private GNode[] edges;
    //是否为有向图
    private boolean isDirect;
    //将顶点与边下标映射
    private HashMap<String, Integer> hash = new HashMap<>();

构造方法和实现邻接矩阵时一样写两个,一个不设定图的类型,默认为无向图,另外一个构造方法可以指定是否为有向图。

    private void init(String[] points) {
        this.points = points.clone();
        int size = this.points.length;
        edges = new GNode[size];
        //初始化哈希表
        for (int i = 0; i < size; i++) {
            hash.put(points[i], i);
        }
    }

    public GraphNode1(String[] points) {
        init(points);
    }
    public GraphNode1(String[] points, boolean isDirect) {
        this.isDirect = isDirect;
        init(points);
    }

最后就是边的添加和邻接表的输出了,添加边的思路其实和邻接矩阵是一样的,不过要注意的一点是,在添加边时,需要在对应的链表上先搜索一遍,如果已经存在终点顶点,那就不用再去添加了。

基本思路如下:

  • 获取两个顶点对应的下标,如果在顶点集合中不存在,直接返回false
  • 添加边时,先判断边集合中是否存在该边,存在就返回false
  • 如果边集合中不存在目标边,则新建结点进行链表头插。
  • 如果是无向图还需要方向添加边。
  • 如果不带权值添加边,以默认权值1进行添加。
    /**
     *
     * @param start 起点
     * @param end 终点
     * @param weight 权重
     * @return 返回值
     */
    public boolean addEdge(String start, String end, int weight) {
        int si = hash.getOrDefault(start, -1);
        int ei = hash.getOrDefault(end, -1);

        if (si == -1 || ei == -1) return false;

        //添加边
        boolean ret = addEdgeFunc(si, end, weight);
        if (!ret) {
            System.out.println("顶点已经存在!");
            return false;
        }
        //如果是无向图需要反向添加
        if (!isDirect) {
            addEdgeFunc(ei, start, weight);
        }
        return true;
    }

    public boolean addEdge(String start, String end) {
        return addEdge(start, end, 1);
    }

    private boolean addEdgeFunc(int si, String end, int weight) {
        GNode cur = edges[si];
        while (cur != null) {
            //查找是否已经存在
            if (end.equals(cur.point)) return false;
            cur = cur.next;
        }
        GNode node = new GNode(end, weight);
        //头插
        cur = edges[si];
        node.next = cur;
        edges[si] = node;
        return true;
    }

邻接表的输出代码实现:

    //输出
    public void print() {
        int n = points.length;
        for (int i = 0; i < n; i++) {
            System.out.print(points[i] + " -> ");
            GNode cur = edges[i];
            while (cur != null) {
                System.out.print("(" + cur.point + "," + cur.weight + ") -> ");
                cur = cur.next;
            }
            System.out.println("null");
        }
    }

最后一步就是测试我们所写的代码是否正确,测试代码如下,与测试邻接矩阵代码基本上一模一样:

    public static void testMa2() {
        String[] s = {"1", "2", "3", "4"};
        //无向图测试
        GraphNode1 gn1 = new GraphNode1(s);
        //无权值
        gn1.addEdge("1", "2");
        gn1.addEdge("1", "3");
        gn1.addEdge("1", "4");
        gn1.addEdge("3", "2");
        gn1.addEdge("4", "3");
        //输出
        System.out.println("无向图,无权值:");
        gn1.print();
        //无向图测试
        GraphNode1 gn2 = new GraphNode1(s);
        //无权值
        gn2.addEdge("1", "2", 4);
        gn2.addEdge("1", "3", 7);
        gn2.addEdge("1", "4", 6);
        gn2.addEdge("3", "2", 5);
        gn2.addEdge("4", "3", 3);
        //输出
        System.out.println("无向图,有权值:");
        gn2.print();

        //有向图测试
        GraphNode1 gn3 = new GraphNode1(s, true);
        //无权值
        gn3.addEdge("1", "2");
        gn3.addEdge("1", "3");
        gn3.addEdge("1", "4");
        gn3.addEdge("3", "2");
        gn3.addEdge("4", "3");
        //输出
        System.out.println("有向图,无权值:");
        gn3.print();
        //有向图测试
        GraphNode1 gn4 = new GraphNode1(s, true);
        //有权值
        gn4.addEdge("1", "2", 4);
        gn4.addEdge("1", "3", 7);
        gn4.addEdge("1", "4", 6);
        gn4.addEdge("3", "2", 5);
        gn4.addEdge("4", "3", 3);
        //输出
        System.out.println("有向图,有权值:");
        gn4.print();
    }

测试结果如下,与我们所画的分析图是一致的: 23