【基础算法】(10)图的概念及相关算法

【基础算法】(10)图的概念及相关算法(第一篇)

Auther: Thomas Shen
E-mail: Thomas.shen3904@qq.com
Date: 2017/12/20
All Copyrights reserved !


1. 图的存储结构:
1.1 邻接矩阵:

使用二维数组来存储图的边的信息和权重,如下图所示的4个顶点的无向图:

这里写图片描述

从上面可以看出,无向图的边数组是一个对称矩阵。所谓对称矩阵就是n阶矩阵的元满足aij = aji。即从矩阵的左上角到右下角的主对角线为轴,右上角的元和左下角相对应的元全都是相等的。

如果换成有向图,则如图所示的五个顶点的有向图的邻接矩阵表示如下:

这里写图片描述

1.2 邻接表:

邻接矩阵是一种不错的图存储结构,但是对于边数相对较少的图,这种结构存在空间上的极大浪费,因此找到一种数组与链表相结合的存储方法称为邻接表。
邻接表的处理方法是这样的:

  1. 图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。
  2. 图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表

如下为无向图的邻接表表示:

这里写图片描述

从图中可以看出,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针。

有向图的邻接表表示:

这里写图片描述

2. 深度优先遍历(DFS):
2.1 基本概念:

它从图中某个结点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到。若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中的所有顶点都被访问到为止。

2.2 算法简述:

基本实现思想:

  1. 访问顶点v;
  2. 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;
  3. 重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。
2.1.1 递归实现:
1. 访问顶点v;visited[v]=1//算法执行前visited[n]=0
2. w=顶点v的第一个邻接点;
3. while(w存在)  
      if(w未被访问)
          从顶点w出发递归执行该算法; 
      w=顶点v的下一个邻接点;
2.1.2 非递归实现:
1. 栈S初始化;visited[n]=02. 访问顶点v;visited[v]=1;顶点v入栈S
3. while(栈S非空)
    x=栈S的顶元素(不出栈);
    if(存在并找到未被访问的x的邻接点w)
        访问w;visited[w]=1;
        w进栈;
    else
        x出栈;
3. 广度优先遍历(BFS):
3.1 基本概念:

它是一个分层搜索的过程和二叉树的层次遍历十分相似,它也需要一个队列以保持遍历过的顶点顺序,以便按出队的顺序再去访问这些顶点的邻接顶点。

基本实现思想:

  1. 顶点v入队列。
  2. 当队列非空时则继续执行,否则算法结束。
  3. 出队列取得队头顶点v;访问顶点v并标记顶点v已被访问。
  4. 查找顶点v的第一个邻接顶点col。
  5. 若v的邻接顶点col未被访问过的,则col入队列。
  6. 继续查找顶点v的另一个新的邻接顶点col,转到步骤(5)。
    直到顶点v的所有未被访问过的邻接点处理完。转到步骤(2)。

广度优先遍历图是以顶点v为起始点,由近至远,依次访问和v有路径相通而且路径长度为1,2,……的顶点。为了使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问,需设置队列存储访问的顶点。

3.2 算法简述:
1. 初始化队列Q;visited[n]=0;
2. 访问顶点v;visited[v]=1;顶点v入队列Q;
3. while(队列Q非空)   
        v=队列Q的对头元素出队;
        w=顶点v的第一个邻接点;
        while(w存在) 
            如果w未访问,则访问顶点w;
            visited[w]=1;
            顶点w入队列Q;
            w=顶点v的下一个邻接点。
4. 代码实现:

采用邻接矩阵存储图的边信息:

/* 
 * 定义图的结构 
 */  
class Graph {  
    static final int MaxNum=20;        //最大节点数目  
    static final int MaxValue=65535;  
    char[] Vertex = new char[MaxNum];         //定义数组,保存顶点信息  

    int GType;   //图的类型0:无向图  1:有向图  
    int VertxNum;              //顶点的数量  
    int EdgeNum;         //边的数量  

    int[][] EdgeWeight = new int[MaxNum][MaxNum];     //定义矩阵保存顶点信息  
    int[] isTrav = new int[MaxNum];            //遍历标志  

}  
//创建邻接矩阵图  
    static void createGraph(Graph g){  
        int i ,  j  ,  k;  
        int weight;     //权  
        char EstartV,  EndV;      //边的起始顶点  

        System.out.println("输入途中各顶点的信息");  
        for(i=0; i < g.VertxNum; i ++)  
        {  
            System.out.println("第" + (i+1) + "个顶点");  
            g.Vertex[i] = (scan.next().toCharArray() )[0];  
        }  
        System.out.println("输入构成个遍的顶点和权值");  
        for(k=0;k<g.EdgeNum;k++)  
        {  
            System.out.println("第" + (k+1) + "条边:");  
            EstartV = scan.next().charAt(0);  
            EndV = scan.next().charAt(0);  
            weight = scan.nextInt();  
            for(i=0; EstartV!=g.Vertex[i] ; i++);           //在已有顶点中查找开始节点  
            for(j=0; EndV != g.Vertex[j]; j++);             //在已有节点上查找终结点  
            g.EdgeWeight[i][j] = weight;       //对应位置保存权重,表示有一条边  
            if(g.GType == 0)               //如果是无向图,在对角位置保存权重  
                g.EdgeWeight[j][i] = weight;  
        }  
    }  
//清空图  
    static void clearGraph(Graph g){  
        int i,j;  
        for(i=0; i< g.VertxNum; i++)  
            for(j =0; j<g.VertxNum; j++)  
                g.EdgeWeight[i][j] = Graph.MaxValue;           //设置矩阵中各院素的值为MaxValue  
    }  
//输出邻接矩阵  
    static void OutGraph(Graph g){  
        int i,j;  
        for(j = 0; j < g.VertxNum;j ++)  
            System.out.print("\t" + g.Vertex[j]);      //在第一行输入顶点信息  
        System.out.println();  

        for(i =0 ;i <g.VertxNum; i ++)  
        {  
            System.out.print( g.Vertex[i]);  
            for(j = 0;j < g.VertxNum; j++)  
            {  
                if(g.EdgeWeight[i][j] == Graph.MaxValue)    //若权值为最大值  
                    System.out.print("\tZ");    //Z 表示无穷大  
                else  
                    System.out.print("\t" + g.EdgeWeight[i][j]);  //输出边的权重  
            }  
            System.out.println();  
        }  
    }  
//遍历图  
    static void DeepTraOne(Graph g,int n){//从第n个节点开始遍历  
        int i;  
        g.isTrav[n] = 1;              //标记为1表示该顶点已经被处理过  
        System.out.println("—>" + g.Vertex[n]); //输出节点数据  
        //添加处理节点的操作  
        for(i = 0; i< g.VertxNum; i++)  
        {  
            //if(g.EdgeWeight[n][i] != g.MaxValue && g.isTrav[n] == 0)  
                        if(g.EdgeWeight[n][i] != g.MaxValue && g.isTrav[i] == 0)  
                {  
                    DeepTraOne(g, i);     //递归进行遍历  
                }  
        }  
    }  
//深度优先遍历  
    static void  DeepTraGraph(Graph g){  
        int i;  
        for(i = 0; i< g.VertxNum; i++)  
        {  
            g.isTrav[i]= 0;  
        }  
        System.out.println("深度优先遍历:");  
        for(i = 0; i< g.VertxNum ; i++)  
        {  
            if(g.isTrav[i] == 0)  
                DeepTraOne(g,i);  
        }  
        System.out.println();  
    }  

主函数:

public static void main(String[] args) {  
        // TODO Auto-generated method stub  
        Graph g = new Graph();  
        System.out.println("输出生成图的类型:");  
        g.GType = scan.nextInt();  //图的种类  

        System.out.println("输入图的顶点数量:");  
        g.VertxNum = scan.nextInt();  

        System.out.println("输入图的边数量:");  
        g.EdgeNum = scan.nextInt();  

        clearGraph(g);          //清空图  
        createGraph(g);      //生成邻接表结构的图  
        System.out.println("该图的邻接矩阵数据如下:");  
        OutGraph(g);        //输出图  
        DeepTraGraph(g);    //深度优先遍历图  
    }  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值