数据结构--图

1、基本概念

图是一种多对多的关系的数据结构
这里写图片描述

1.2 图的定义

1.2.1 无向边
若顶点 vi vj 之间的边没有方向,这条边称为无向边。如果任意两个顶点之间的边都是无向边,则该图称为无向图。
G=(V, {E})   V={A, B, C}   E={(A,B), (B,C), (C,A)}

1.2.2 有向边
若顶点 vi vj 之间的边有方向,这条边称为有向边,也成为弧。如果任意两个顶点之间的边都是有向边,则该图称为有向图。
G=(V, {E})   V={A, B, C}   E={<A,B>, <B,C>, <C,A>}    <A,B> A称为弧头,B称为弧尾,且顺序不能更改
无向边用() 有向边用<>

1.2.3 完全图
如果无向图的任意两个点之间都存在边,则该图为无向完全图。 n个顶点有(n*(n-1))/2条边
如果有向图的任意两个点都存在互为相反的弧,则该图为有向完全图。n个顶点有n*(n-1)条弧

1.2.4 稀疏图、稠密图
有很少条边或弧的图称为稀疏图,反之称为有向图

1.2.5 权、网
图的边或弧有与它相关的数字,这个数字称为权。带权的图称为网

1.2.6 子图
假设G=(V, {E})和G1=(V1, {E1}) 如果 V1V E1E ,则G1是G的子图

1.3 图的顶点与边的关系

1.3.1 无向图
有G = (V, {E}),如果边( vi , vj ) E,则顶点 vi , vj 相邻接。边( vi , vj )依附于顶点 vi , vj
顶点 vi 的度是和 vi 相关联的边的数目,记为TD(v)

1.3.2 有向图
有G = (V, {E}),如果边< vi , vj > E。以顶点 vi 为头的弧称为入度,记为ID(v)。以顶点 vi 为尾的度称为出度,记为OD(v)

1.3.3 路径
图中顶点到顶点之间的路径不是唯一的
路径的长度是路径上边或者弧的数目
回环:第一个顶点与最后一个顶点相同
简单路径:序列中,顶点不重复出现的路径
简单回路:除了第一个顶点与最后一个顶点之外,其余顶点不重复的回路

1.4 连通图

1.4.1 无向图
在无向图G中,如果顶点v,v1有路径,则成v和v1是连通的
对于图中,任意两个顶点( vi , vj ) E, vi , vj 都是连通的,则称该图为连通图
无向图中的极大连通子图称为连通分量
1.4.2 有向图
在有向图G中,如果对于每一对( vi , vj ) E, vi != vj ,从 vi vj 和从 vj vi 都存在路径,则称G为强连通图。有向图中的极大强连通子图称为有向图的强连通分量

1.5 连通图的生成树

1.5.1 无向图
连通图的生成树是一个极小的连通子图,它含有图中全部的n个顶点,但只有足以构成一棵树的n-1条边
图中的每个点都连通且只有n-1条边,即一颗树的形态
1.5.2 有向图
如果一个图恰有一个顶点入度为0,其余顶点的入度都为1,则是一颗有向树
一个有向图的生成森林由若干颗有向树组成,含有图中全部顶点,但只有足以构成若干颗不相交的有向树的弧。即一个有向图可以分解为一个森林

2、图的存储结构

2.1 邻接矩阵

2.1.1 无向图
使用两个数组来表示图,一个一维数组存储顶点,一个二维数组(邻接矩阵)存储图中的边或者弧
若图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为

例如
这里写图片描述
v0 , v1 =1存在边, v1 , v3 =0不存在边
无向图的边数组是一个对称矩阵
特点:
某点的度为这个顶点所在行的元素之和,如 v0 有三个边,矩阵中的和为3
某点的邻接点就是当前行为1的点

2.1.2 有向图
这里写图片描述
有向图的入度为当前元素所在列的和
有向图的出度为当前元素所在行的和

2.1.3 带权的图

W表示权值
0表示自身
若两个顶点不存在边,使用无限大表示

2.2 邻接表

邻接矩阵是一个不错的图的存储结构,但是对于顶点较少的图,浪费空间。
所以使用数组存储每个元素,元素的邻接点使用单链表的形式。
这里写图片描述
求某个顶点的度:数组所在下标的链表个数
求某个点的邻接点:同上

2.2.1 有向图的邻接表
有向图是有方向的,所以邻接表也是有方向的。
所以邻接表只能表示顶点的出度,如果想要获取入度,必须遍历整个图。也可以创建逆邻接表。

2.3 十字链表


十字链表是为了解决邻接表有向图的存储问题。十字链表就是将有向图的邻接表和逆邻接表结合在一起


链表中的数据结构:tailvex为弧起点在顶点表中的下标,headvex为弧终点在顶点表中的下标。即用tailvex和headvex表示当前顶点的弧,且为出度。headlink为入边表指针域,指向弧终点相同的下一条边。taillink为出边表指针域,指向弧起点相同的指针域。即实线部分代表出度,虚线代表入度。

2.4 邻接多重表

用邻接表表示无向图时,若要删除一条边(0,2),需要到两个链表删除,比较繁琐。
这里写图片描述
邻接多重表就是仿照十字链表的方式
这里写图片描述
链表的数据结构中ivex和jvex是某条边依附的两个顶点在顶点表中的下标。ilink是指向依附ivex顶点的下一条边。jlink是指向依附jvex顶点的下一条边。
若要删除边v0,v2 只需要将6号的jlink和9号的jlink置null

2.5 边集数组

边集数组由两个一维数组组成。一个存放顶点,另一个存放边信息。边信息起点下标(begin)、终点下标(end)、权组成
这里写图片描述

3、图的遍历

从图中某一顶点出发,访遍图中其余顶点,且使每个顶点仅被访问一次,之一过程称为图的遍历。
图遍历的次序分为深度优先遍历、广度优先遍历

3.1 深度优先遍历(DFS)

从图中某个顶点v出发,访问此顶点,然后从v的未访问的邻接点出发遍历图,知道图中所有和v有路径相同的顶点被访问到。类似二叉树的先序遍历

3.2 广度优先遍历(BFS)

按层进行遍历,类似二叉树的层遍历

public class GraphTest {
    //数据数组
    Vertex vertex[] = new Vertex[100];
    int verNum=0;   //顶点数
    int edgeNum=0;  //边数
    //是否访问标记
    boolean visited[] = new boolean[100];

    //测试数据
    private String item[] = {"v0", "v1", "v2", "v3"};
    private String edge[][] = {{"v0","v1"}, {"v1","v2"}, {"v2","v3"}, {"v3","v0"}, {"v0","v2"}};

    //数据类
    class Vertex {
        String data;
        Edge first;
    }

    //边节点类
    class Edge{
        int index;//存放数据下标
        Edge next;
    }

    //创建图
    public void createGraph(){
        verNum = item.length;
        //构建顶点
        for (int i = 0; i < item.length; i++) {
            vertex[i] = new Vertex();

            vertex[i].data = item[i];
            vertex[i].first = null;
        }

        edgeNum = edge.length;

        //构建边
        for (int i = 0; i < edge.length; i++) {
            //构建下标为edge[i][0]的数据
            Edge node0 = new Edge();
            int node0Index = getIndex(edge[i][1]);

            node0.index = getIndex(edge[i][0]);
            //链表头插法
            node0.next = vertex[node0Index].first;
            vertex[node0Index].first = node0;

            //构建下标为edge[i][1]的数据
            Edge node1 = new Edge();
            int node1Index = getIndex(edge[i][0]);

            node1.index = getIndex(edge[i][1]);
            node1.next = vertex[node1Index].first;
            vertex[node1Index].first = node1;
        }
    }

    //邻接表深度优先遍历 递归
    public void DFSTraverse(){
        for (int i = 0; i < verNum; i++)
            visited[i] = false;

        for (int i = 0; i < verNum; i++)
            if(!visited[i]) //如果没被访问过 就进行遍历 如果是连通图,递归一次就够了
                DFS(i);
    }

    //深度优先遍历的递归方法
    private void DFS(int i) {
        visited[i] = true;
        System.out.print(vertex[i].data + "  ");

        Edge edge = vertex[i].first;
        while(edge != null){
            if(!visited[edge.index]) //如果没被访问过 就进行递归
                DFS(edge.index);

            edge = edge.next;
        }
    }

    //邻接表的广度优先遍历
    public void BFSTraverse() {
        for (int i = 0; i < verNum; i++)
            visited[i] = false;

        Queue<Vertex> queue = new LinkedList<Vertex>();
        Edge edge = null;

        for (int i = 0; i < verNum; i++) {  //遍历所有点,避免漏掉未连通的顶点
            if (!visited[i]) {
                visited[i] = true;

                System.out.print(vertex[i].data + "  ");

                queue.offer(vertex[i]);
                while (!queue.isEmpty()) {
                    edge = queue.poll().first;   //获取当前顶点的第一个边信息

                    while(edge != null) {   //遍历链表的所有连通边的顶点,并将顶点放入队列
                        if(!visited[edge.index]) {
                            visited[edge.index] = true;
                            System.out.print(vertex[edge.index].data + "  ");

                            queue.offer(vertex[edge.index]);
                        }

                        edge = edge.next;
                    }
                }
            }
        }
    }

    //根据顶点获取下标
    private int getIndex(String data){
        for (int i = 0; i < item.length; i++)
            if (item[i].equals(data))
                return i;
        return -1;
    }
}

调用:

public static void main(String[] args) {
        GraphTest graph = new GraphTest();
        graph.createGraph();//创建

        graph.DFSTraverse();//深度优先巴黎
        System.out.println();

        graph.BFSTraverse();//广度优先遍历
        System.out.println();
    }

参考:大话数据结构

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值