图的实现及深度优先遍历和广度优先遍历

1为什么要有图?

1、线性表局限于一个直接前驱和一个直接后继的关系

2、树也只能有一个直接前驱即父节点

3、当我们需要多对多的关系时,就需要图

2、图的基本介绍

1、图是一种数据和结构,其中节点可以具有多个或零个相邻元素。两个结点之间的连接称为边。结点可以称为顶点

在这里插入图片描述

3、图的基本概念

1、顶点(vertex)

2、边(edge)

3、路径

4、无向图:顶点之间的连接没有方向

5、有向图:顶点之间连接有方向

6、带权图:这种边带权值的图也叫网

在这里插入图片描述

在这里插入图片描述

4、图的表示方式
1、邻接矩阵

1、邻接矩阵:表示图形中顶点之间邻接关系的矩阵,对于n个顶点的图而言,矩阵是的row和col表示的是 1…n 个点(1表示量顶点之间是邻接的)

在这里插入图片描述

2、邻接表

1、邻接矩阵需要为每个顶点分配 n 个边的空间,其实很多边是不存在,会造成空间的一定损失

2、邻接表是实现只关系存在的边,不关心不存在的边,因此没有浪费,邻接表由数据+链表组成

在这里插入图片描述

5、图的入门

1、要求:实现如下图结构

在这里插入图片描述

2、思路分析:存储顶点 String 使用 ArrayList ;保存矩阵 二维数组

3、代码实现

public class Graph {
    private List<String> vertexList;//存储顶点的集合
    private int[][] edges;//存储图对应的邻接矩阵
    private int numOfEdges;//图的边数
    public Graph(int n){
        this.vertexList=new ArrayList<>();
        this.edges=new int[n][n];
    }
    //添加顶点
    public void insertVertex(String vertex){
        vertexList.add(vertex);
    }
    //添加边
    public void insertEdge(int v1,int v2,int weight){
        edges[v1][v2]=weight;
        edges[v2][v1]=weight;
        numOfEdges++;
    }
    //返回顶点数目
    public int getNumOfVertex(){
        return vertexList.size();
    }
    //展示图
    public void showGraph(){
        for (int[] edge : edges) {
            System.err.println(Arrays.toString(edge));
        }
    }
    //返回边的数目
    public int getNumOfEdges(){
        return numOfEdges;
    }
    //返回 v1 v2 的权值
    public int getWeight(int v1,int v2){
        return edges[v1][v2];
    }
    //返回下标对应的值
    public String getValueByIndex(int i){
        return vertexList.get(i);
    }
}
6、图的深度优先遍历(Depth First Search)
1、图的深度优先遍历的基本思想

1、深度优先遍历,从初始访问结点出发,初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点,可以这样理解:每次访问完当前节点后首先访问当前结点的第一个邻接结点。

2、这样的访问策略是优先往纵向挖掘深入,而不是对一个结点的邻接结点进行横向访问。

3、深度优先遍历是一个递归过程。

2、深度优先遍历算法步骤

1、访问初始结点 v ,并标记 v 已经访问过了

2、查找 v 的第一个邻接结点 w

3、若 w 存在,则继续执行 4 ,如果 w 不存在,则回到第一步,将从 v 的下一个结点继续

4、若 w 未被访问,对 w 进行深度优先遍历递归

5、若 w 被访问过了,查找结点 v 的 w 邻接结点的下一个邻接结点,转到 3

3、代码实现
//得到第一个邻接结点的下标
public int getFirstNeighbor(int index){
    for(int i=0;i<vertexList.size();i++){
        if(edges[index][i]>0){
            return i;
        }
    }
    return -1;
}
//根据前一个邻接结点的下标来获取下一个邻接结点
public int getNextNeighbor(int v1,int v2){
    for(int i=v2+1;i<vertexList.size();i++){
        if(edges[v1][i]>0){
            return i;
        }
    }
    return -1;
}
public void dfs(boolean[] isVisted,int i){
    System.out.print(getValueByIndex(i)+" ");
    isVisted[i]=true;
    int w = getFirstNeighbor(i);
    while(w!=-1){
        if(!isVisted[w]){
            dfs(isVisted,w);
        }
        w=getNextNeighbor(i,w);
    }
}
public void dfs(){
    //for 循环用来处理独立的结点,就是没有边的结点
    for(int i=0;i<vertexList.size();i++){
        if(!isVisted[i]){
            dfs(isVisted,i);
        }
    }
}
7、图的广度优先遍历
1、图的广度优先遍历的基本思想

1、图的广度优先搜索(Broad First Search)

2、类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的结点的顺序,以便按这个顺序来访问这些结点的邻接结点

2、图的广度优先遍历算法步骤

1、初始化结点 v ,并标记为已访问

2、结点 v 入队

3、当队列非空时,继续执行,否则算法结束

4、出队列,取得对头结点 u

5、查找结点 u 的第一个邻接结点 w

6、若结点 u 的邻接结点 w 不存在,则转到步骤 3 ,否则循环执行以下三个步骤

​ 6.1、若结点 w 尚未被访问,则访问结点 w 并标记为已访问,

​ 6.2、结点 w 入队

​ 6.3、查找结点 u 的继 w 邻接结点后的下一个邻接结点 w ,转到 6

3、代码实现
public void bfs(boolean[] isVisted,int i){
    Queue<Integer> queue=new LinkedList<>();
    // 1、
    System.out.println(getValueByIndex(i)+" ");
    isVisted[i]=true;
    // 2、
    queue.offer(i);
    // 3、
    while(!queue.isEmpty()){
        // 4、
        Integer u = queue.poll();
        // 5、
        int w = getFirstNeighbor(u);
        while(w!=-1){
            // 6.1、
            if(!isVisted[w]){
                System.out.println(getValueByIndex(w)+" ");
                isVisted[w]=true;
                // 6.2、
                queue.offer(w);
            }
            // 6.3、
            w=getNextNeighbor(u,w);
        }
    }
}

public void bfs(){
    //for 循环用来处理独立的结点,就是没有边的结点
    for(int i=0;i<vertexList.size();i++){
        if(!isVisted[i]){
            bfs(isVisted,i);
        }
    }
}
  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值