Java数据结构:图

作者: Patarw | 来源:发表于2020-07-21 18:02 被阅读0次

一、为什么要有图

  • 线性表局限于一个直接前驱和一个直接后继的关系
  • 树也只能有一个直接前驱也就是父节点
  • 当我们需要表示多对多的关系时,就需要用到图这种数据结构
基本介绍
  • 图是一种数据结构,其中结点可以具有零个或多个相邻元素。两个结点之间的连接称为边(edge); 结点也可以称为顶点(vertex)。
  • 路径: 比如从 D -> C 的路径有
    1. D->B->C
    2. D->A->B->C
  • 1.无向图: 顶点之间的连接没有方向,比如A-B,
    即可以是 A-> B 也可以 B->A .
  • 2.有向图: 顶点之间的连接有方向,比如A-B,
    只能是 A-> B 不能是 B->A .
  • 3.带权图:这种边带权值的图也叫网

二、图的表示方式

图的表示方式有两种:二维数组表示(邻接矩阵)链表表示(邻接表)

  • 1.邻接矩阵表示:邻接矩阵是表示图形中顶点之间相邻关系的矩阵
  • 2.邻接表表示:邻接矩阵需要为每个顶点都分配n个边的空间,其实有很多边都是不存在,会造成空间的一定损失.而邻接表的实现只关心存在的边,不关心不存在的边。因此没有空间浪费,邻接表由数组+链表组成。
  • 代码实现邻接矩阵:

    以这个图为例
       public class ArrayQueue {

private ArrayList<String> vertexList; //存储顶点集合
private int[][] edgs; //存储对应邻接矩阵
private int numOfEdgs; //表示边的数目
private boolean[] isVisited; //记录某个节点是否被访问
public static void main(String[] args) {    
    int n = 5;
    String VertexValue[] = {"A","B","C","D","E"};
    ArrayQueue a = new ArrayQueue(n);
    for(String val : VertexValue) {
        a.insertVertex(val);
    }
    //A-B A-C B-C B-D B-E
    a.insertEdge(0, 1, 1);
    a.insertEdge(0, 2, 1);
    a.insertEdge(1, 2, 1);
    a.insertEdge(1, 3, 1);
    a.insertEdge(1, 4, 1);
    a.print();
    a.dfs();
}

//构造器,初始化实例
public ArrayQueue(int n) {
    edgs = new int[n][n];
    vertexList = new ArrayList<>(n);
    numOfEdgs = 0;
    isVisited = new boolean[n];
}

//插入节点
public void insertVertex(String vertex) {
    vertexList.add(vertex);
}

//添加边
public void insertEdge(int v1,int v2,int weight) {
    edgs[v1][v2] = weight;
    edgs[v2][v1] = weight;
    numOfEdgs++;
}

//返回节点个数
public int getNumOfVertex() {
    return vertexList.size();
}

//得到边的数目
public int getNumOfEdgs() {
    return numOfEdgs;
}

//返回i下表对应的数据
public String getValueByIndex(int i) {
    return vertexList.get(i);
}

//返回v1,v2的权值
public int getWeight(int v1,int v2) {
    return edgs[v1][v2];
}

//打印图
public void print() {
    for(int[] a : edgs) {
        for(int i = 0;i < a.length;i++) {
            System.out.print(a[i] + " ");
        }
        System.out.println();
    }
}

}
  • 打印结果如图:

三、图的遍历方式

  • 所谓图的遍历,即是对结点的访问。一个图有那么多个结点,如何遍历这些结点,需要特定策略,一般有两种访问策略: (1)深度优先遍历 (2)广度优先遍历
1、图的深度优先遍历 DFS (Depth First Search)
  • 二叉树的前序、中序、后序遍历,本质上也可以认为是深度优先遍历。
  • 假设你去参观景点,把每个节点看作为一个景点,那么怎么规划参观路线呢?
  • DFS就相当于是一头扎到底的玩法。我们选择一条支路,尽可能不断地深入,如果遇到死路就往回退,回退过程中如果遇到没探索过的支路,就进入该支路继续深入。(这里看出来DFS肯定要用到递归回溯)
  • 在图中,我们首先选择景点1的这条路,继续深入到景点4、景点5、景点3、景点6,发现后面没有其他景点了(景点旁边的数字代表探索次序):
  • 于是,我们退回到景点1,然后探索景点7,景点8,又走到了死胡同。于是,退回到景点7,探索景点10:
  • 按照这个思路,我们再退回到景点1,探索景点9,最后再退回到景点0,后续依次探索景点2,终于玩遍了整个游乐场:
  • 代码实现
      //得到下一个邻接节点的下标
public int getFirstNeighbor(int index) {
    for(int j = 0;j < vertexList.size();j++) {
        if(edgs[index][j] > 0) {
            return j;
        }
    }
    return -1;
}

//获取下一位
public int getNextNeighbor(int v1,int v2) {
    for(int j = v2 + 1;j < vertexList.size();j++) {
        if(edgs[v1][j] > 0) {
            return j;
        }
    }
    return -1;
}

//深度优先遍历算法
public void dfs(boolean[] isVisited,int i) {
    //首先访问该节点
    System.out.print(getValueByIndex(i) + "->");
    isVisited[i] = true; //设置节点为已访问
    int w = getFirstNeighbor(i);
    while(w != -1) {
        if(!isVisited[w]) {
            dfs(isVisited,w);
        }
        w = getNextNeighbor(i,w);
    }
}

//对dfs进行一个重载,遍历我们所有的节点,并进行dfs
public void dfs() {
    //遍历所有节点,进行dfs[回溯]
    for(int i = 0;i < getNumOfVertex();i++) {
        if(!isVisited[i]) {
            dfs(isVisited,i);
        }
    }
}
2、图的广度优先遍历 BFS (Broad First Search)

二叉树的层次遍历,本质上也可以认为是深度优先遍历。

  • 在图中,我们首先探索景点0的相邻景点1、2、3、4
  • 接着,我们探索与景点0相隔一层的景点7、9、5、6:
  • 最后,我们探索与景点0相隔两层的景点8、10:
  • 代码实现:
      //广度优先遍历算法
public void bfs(boolean[] isVisited,int i) {
    int u = 0;
    int w = 0;
    LinkedList q = new LinkedList(); //用链表模拟队列
    System.out.print(getValueByIndex(i) + "=>");
    isVisited[i] = true;
    q.addLast(i);
    
    while(!q.isEmpty()) {
        u = (Integer)q.removeFirst();
        w = getFirstNeighbor(u);
        while(w != -1) {
            if(!isVisited[w]) {
                System.out.print(getValueByIndex(w) + "=>");
                isVisited[w] = true;
                q.addLast(w);
            }
            w = getNextNeighbor(u,w);
        }
    }
}

五、图的深度优先VS 广度优先

  • 深度优先遍历顺序为 1->2->4->8->5->3->6->7
  • 广度优先算法的遍历顺序为:1->2->3->4->5->6->7->8

相关文章

  • Java核心类库—— 数据结构

    Java核心类库-------数据结构体系图 1.数据结构 2.栈 3.哈希表

  • 集合概述

    一:集合的UML类图 二:集合工具的分析 (Java集合是java提供的工具) 常用的数据结构: 集合、链表、队列...

  • Java数据结构:图

    一、为什么要有图 线性表局限于一个直接前驱和一个直接后继的关系 树也只能有一个直接前驱也就是父节点 当我们需要表示...

  • Java数据结构算法(二)栈和队列

    本文旨作于收集整理使用!! 导航 Java数据结构算法(一)链表 Java数据结构算法(三)树 Java数据结构算...

  • Java(Android)数据结构汇总 -- 总纲

    目录:Java(Android)数据结构汇总(一)-- List(上)Java(Android)数据结构汇总(一)...

  • Java数据结构

    Java 数据结构 Java工具包提供了强大的数据结构。在Java中的数据结构主要包括以下几种接口和类: 枚举(E...

  • java 数据结构(collections)

    JAVA中常用的数据结构(java.util. 中) java中有几种常用的数据结构,主要分为Collection...

  • 图表的数据返回格式

    柱状图、折线图、雷达图的数据结构 饼状图、圆环图、漏斗图、仪表盘的数据结构 地图的数据结构 散点图的数据结构 sc...

  • java集合框架总结

    一 java 集合框架图 二 List 2.1 ArrayList 底层数据结构:线性表(数组)线程安全:否核心字...

  • 文集总目录

    数据结构 [Java] 目录算法 [Java] 目录LeetCode [Java] 目录设计模式 [Java] 目...

网友评论

    本文标题:Java数据结构:图

    本文链接:https://www.haomeiwen.com/subject/ebabkktx.html