数据结构——图(1),图的存储结构Java实现

本文深入介绍了图的基本概念、存储结构及其实现方式。详细对比了邻接矩阵与邻接表的特点,并提供了Java代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

图概述

图是一种重要的数据结构,基本概念包括:顶点,边,有向,无向,权,路径回路,连通域,邻接点,度,入边,出边,入度,出度等等,很好理解,不赘述,可以参考这篇博客:http://www.cnblogs.com/skywang12345/p/3691463.html

图的存储结构

图可以使用两种存储结构,分别是邻接矩阵和邻接表。

邻接矩阵以矩阵的形式存储图所有顶点间的关系。邻接矩阵具有以下特点:

1,邻接矩阵是正矩阵,即横纵维数相等。
2,矩阵的每一行或一列代表一个顶点,行与列的交点对应这两个顶点的边。
3,矩阵的点代表边的属性,1代表有边,0代表无边,所以矩阵的对角线都是0,因为对角线上对应的横纵轴代表相同的顶点,边没有意义。
4,如果是无向图,那么矩阵是对称矩阵;如果是有向图则不一定。
5,如果是有权图,矩阵点数值可以是权值。
6,邻接矩阵表示图的关系非常清晰,但消耗空间较大。

邻接表是以一组链表来表示顶点间关系,有以下特点:

1,邻接表示一个有但链表组成的数组
2,图中的每一个顶点都有一个链,数组的大小等于图中顶点的个数。
3,无向图的链的第一个元素是本顶点,后继分别连接着和这个顶点相连的顶点;有向图的链第一个顶点是本顶点,后继是以本顶点为起点的边的终点。
4,如果是有权图,可以在节点元素中设置权值属性
5,邻接链表关系表示不如邻接矩阵清晰,数据结构相对复杂,但节省空间。

Java实现

邻接矩阵无向图

public class MatrixNDG {

    int size;//图顶点个数
    char[] vertexs;//图顶点名称
    int[][] matrix;//图关系矩阵

    public MatrixNDG(char[] vertexs,char[][] edges){
        size=vertexs.length;
        matrix=new int[size][size];//设定图关系矩阵大小
        this.vertexs=vertexs;

        for(char[] c:edges){//设置矩阵值
            int p1 = getPosition(c[0]);//根据顶点名称确定对应矩阵下标
            int p2 = getPosition(c[1]);

            matrix[p1][p2] = 1;//无向图,在两个对称位置存储
            matrix[p2][p1] = 1;
        }

    }

    //图的遍历输出
    public void print(){
        for(int[] i:matrix){
            for(int j:i){
                System.out.print(j+" ");
            }
            System.out.println();
        }
    }

    //根据顶点名称获取对应的矩阵下标
    private int getPosition(char ch) {
        for(int i=0; i<vertexs.length; i++)
            if(vertexs[i]==ch)
                return i;
        return -1;
    }

    public static void main(String[] args) {
        char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G','H','I','J','K'};
        char[][] edges = new char[][]{
            {'A', 'C'}, 
            {'A', 'D'}, 
            {'A', 'F'}, 
            {'B', 'C'}, 
            {'C', 'D'}, 
            {'E', 'G'}, 
            {'D', 'G'},
            {'I','J'},
            {'J','G'},};
        MatrixNDG pG;
        // 自定义"图"(输入矩阵队列)
        // 采用已有的"图"
        long start=System.nanoTime();

        for(int i=0;i<10000;i++){
            pG = new MatrixNDG(vexs, edges);
            //pG.print();   // 打印图 
        }

        long end=System.nanoTime();

        System.out.println(end-start);
    }
}

邻接矩阵有向图

邻接矩阵有向图和邻接矩阵无向图结构一样,只是在存储矩阵值时只存储对应方向的点。

public class MatrixDG {
    int size;
    char[] vertexs;
    int[][] matrix;

    public MatrixDG(char[] vertexs,char[][] edges){
        size=vertexs.length;
        matrix=new int[size][size];
        this.vertexs=vertexs;

        //和邻接矩阵无向图差别仅仅在这里
        for(char[] c:edges){
            int p1 = getPosition(c[0]);
            int p2 = getPosition(c[1]);

            matrix[p1][p2] = 1;
        }

    }

    public void print(){
        for(int[] i:matrix){
            for(int j:i){
                System.out.print(j+" ");
            }
            System.out.println();
        }
    }

    private int getPosition(char ch) {
        for(int i=0; i<vertexs.length; i++)
            if(vertexs[i]==ch)
                return i;
        return -1;
    }



    public static void main(String[] args) {
        char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G','H','I','J','K'};
        char[][] edges = new char[][]{
            {'A', 'C'}, 
            {'A', 'D'}, 
            {'A', 'F'}, 
            {'B', 'C'}, 
            {'C', 'D'}, 
            {'E', 'G'}, 
            {'D', 'G'},
            {'I','J'},
            {'J','G'},};
        MatrixDG pG;
        // 自定义"图"(输入矩阵队列)
        //pG = new MatrixUDG();
        // 采用已有的"图"
        pG = new MatrixDG(vexs, edges);

        pG.print();
    }

}

输出结果是非对称矩阵。

0 0 1 1 0 1 0 0 0 0 0 
0 0 1 0 0 0 0 0 0 0 0 
0 0 0 1 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 0 0 0 
0 0 0 0 0 0 1 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 1 0 
0 0 0 0 0 0 1 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 

邻接表无向图

public class ListNDG {

    Vertex[] vertexLists;//邻接表数组
    int size;

    class Vertex{//邻接表节点类,单链表数据结构
        char ch;
        Vertex next;

        Vertex(char ch){//初始化方法
            this.ch=ch;
        }
        void add(char ch){//加到链表尾
            Vertex node=this;
            while(node.next!=null){
                node=node.next;
            }
            node.next=new Vertex(ch);
        }
    }

    public ListNDG(char[] vertexs,char[][] edges){

        size=vertexs.length;
        this.vertexLists=new Vertex[size];//确定邻接表大小
        //设置邻接表头节点
        for(int i=0;i<size;i++){
            this.vertexLists[i]=new Vertex(vertexs[i]);
        }
        //存储边信息
        for(char[] c:edges){
           int p1=getPosition(c[0]);
           vertexLists[p1].add(c[1]);
           int p2=getPosition(c[1]);
           vertexLists[p2].add(c[0]);
        }

    }

    //跟据顶点名称获取链表下标
    private int getPosition(char ch) {
        for(int i=0; i<size; i++)
            if(vertexLists[i].ch==ch)
                return i;
        return -1;
    }

    //遍历输出邻接表
    public void print(){
       for(int i=0;i<size;i++){
           Vertex temp=vertexLists[i];
           while(temp!=null){
               System.out.print(temp.ch+" ");
               temp=temp.next;
           }
           System.out.println();
       }
    }

    public static void main(String[] args){
        char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G','H','I','J','K'};
        char[][] edges = new char[][]{
            {'A', 'C'}, 
            {'A', 'D'}, 
            {'A', 'F'}, 
            {'B', 'C'}, 
            {'C', 'D'}, 
            {'E', 'G'}, 
            {'D', 'G'},
            {'I','J'},
            {'J','G'},};

        ListNDG pG;

        long start=System.nanoTime();

        for(int i=0;i<10000;i++){
            pG = new ListNDG(vexs, edges);
            //pG.print();   // 打印图 
        }

        long end=System.nanoTime();

        System.out.println(end-start);

    }

}

邻接表有向图

和邻接矩阵一样,邻接表有向图和邻接表无向图数据结构一样,只是在存储时一个需要存两次,一个需要存一次。

public class ListDG {
    Vertex[] vertexLists;
    int size;

    class Vertex{
        char ch;
        Vertex next;

        Vertex(char ch){
            this.ch=ch;
        }
        void add(char ch){
            Vertex node=this;
            while(node.next!=null){
                node=node.next;
            }
            node.next=new Vertex(ch);
        }


    }

    public ListDG(char[] vertexs,char[][] edges){

        size=vertexs.length;
        this.vertexLists=new Vertex[size];
        for(int i=0;i<size;i++){
            this.vertexLists[i]=new Vertex(vertexs[i]);
        }

        for(char[] c:edges){
           int p=getPosition(c[0]);
           vertexLists[p].add(c[1]);
        }

    }

    private int getPosition(char ch) {
        for(int i=0; i<size; i++)
            if(vertexLists[i].ch==ch)
                return i;
        return -1;
    }

    public void print(){
       for(int i=0;i<size;i++){
           Vertex temp=vertexLists[i];
           while(temp!=null){
               System.out.print(temp.ch+" ");
               temp=temp.next;
           }
           System.out.println();
       }
    }

    public static void main(String[] args){
        char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G','H','I','J','K'};
        char[][] edges = new char[][]{
            {'A', 'C'}, 
            {'A', 'D'}, 
            {'A', 'F'}, 
            {'B', 'C'}, 
            {'C', 'D'}, 
            {'E', 'G'}, 
            {'D', 'G'},
            {'I','J'},
            {'J','G'},};

        ListDG pG;

        long start=System.nanoTime();

        for(int i=0;i<10000;i++){
            pG = new ListDG(vexs, edges);
            //pG.print();   // 打印图 
        }

        long end=System.nanoTime();

        System.out.println(end-start);

    }
}

邻接表再讨论

在邻接表存储结构的实现中,我创建了一个内部类Vertex来实现单链表。我稍微感觉到有些不优雅,因为在图数据结构中又引入了一个数据结构,而单链表是可以用Java原生的LinkedList来实现的,于是便有了邻接表有向图的第二次实现:

import java.util.LinkedList;

public class ListDG2 {

    LinkedList<Character>[] vertexLists;
    int size;

    public ListDG2(char[] vertexs,char[][] edges){
        size=vertexs.length;
        this.vertexLists=new LinkedList[size];

        for(int i=0;i<size;i++){
            this.vertexLists[i]=new LinkedList<Character>();
            vertexLists[i].add(vertexs[i]);
        }

        for(char[] c:edges){
            int p=getPosition(c[0]);
            this.vertexLists[p].add(c[1]);
         }

    }

    private int getPosition(char ch) {
        for(int i=0; i<size; i++)
            if(vertexLists[i].get(0)==ch)
                return i;
        return -1;
    }

    public void print(){
       for(int i=0;i<size;i++){
           LinkedList<Character> temp=vertexLists[i];
           for(int j=0;j<temp.size();j++){
               System.out.print(temp.get(j)+" ");
           }
           System.out.println();
       }
    }

    public static void main(String[] args){
        char[] vexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G','H','I','J','K'};
        char[][] edges = new char[][]{
            {'A', 'C'}, 
            {'A', 'D'}, 
            {'A', 'F'}, 
            {'B', 'C'}, 
            {'C', 'D'}, 
            {'E', 'G'}, 
            {'D', 'G'},
            {'I','J'},
            {'J','G'},};

        ListDG2 pG;

        long start=System.nanoTime();

        for(int i=0;i<10000;i++){
            pG = new ListDG2(vexs, edges);
            //pG.print();   // 打印图 
        }

        long end=System.nanoTime();

        System.out.println(end-start);
    }

}

和之前大同小异,只是用LinkedList来代替自定义的数据结构,我也尝试实用HashMap来代替getPosition方法,避免每加入一条变信息都要从头遍历寻找下标。我使用了类似下面的代码来简单测试图初始化的效率。实验的结果发现,效率并没有提升反而下降,当然这不是一个全面的测试,没有考虑其他相关的图数据结构操作,也没有考虑图的大小和边的数量的影响,但总体来说,使用自建的内部类和遍历方法的效率在使用LinkedList和HashMap的两倍以上。这略微有些反直觉,想来可能是因为实际是Java集合类因为要实现通用强大的功能,变得比较重型,反而不如自建的数据结构和算法快捷轻巧。

        long start=System.nanoTime();

        for(int i=0;i<10000;i++){
            pG = new ListDG2(vexs, edges);
            //pG.print();   // 打印图 
        }

        long end=System.nanoTime();
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值