JAVA-数据结构与算法-图

205 阅读2分钟

写在前面

  • 处理多对多的关系
  • 边,两个节点之间的关系;节点,也可以叫做顶点
  • 无向图,顶点之间连接没有方向
  • 有向图,顶点之间有方向
  • 带权图,边带权值
  • 表现方式,邻接矩阵(二维数组),1表示连通;邻接表(数组+链表),该条链表中有的元素,代表跟这个点连通,比如arr[0]的链表元素为1,2,3,4,表示0跟这四个点都连通

邻接矩阵实现图

public class Graph {

    private ArrayList<String> vertexList;
    private int[][] edges;
    private int numOfEdges;

    public Graph(int n) {
        //初始化矩阵和vertexList
        edges = new int[n][n];
        vertexList = new ArrayList<>();
        numOfEdges = 0;
    }
    //返回节点的个数
    public int getNumOfVertex() {
        return vertexList.size();
    }
    //得到边的个数
    public int getNumOfEdges() {
        return numOfEdges;
    }
    //返回节点对应的数据
    public String getValueByIndex(int i) {
        return vertexList.get(i);
    }
    //返回v1和v2的权值
    public int getWeight(int v1, int v2) {
        return edges[v1][v2];
    }
    //显示图对应的矩阵
    public void showGraph() {
        for (int[] edge : edges) {
            System.out.println(Arrays.toString(edge));
        }
    }
    //插入节点
    public void insertVertex(String vertex) {
        vertexList.add(vertex);
    }
    //添加边
    /**
     * @param v1 点的下标
     * @param v2 点的下标
     * @param weight 1/0是否连通
     */
    public void insertEdge(int v1, int v2, int weight) {
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOfEdges ++;
    }
}

图的深度优先遍历

  • 纵向
  • Depth First Search,DFS,从初始访问节点出发,初始访问节点可能有多个邻接节点;首先访问第一个节点,访问第一个节点的邻接点,再以这个邻接点访问下一个节点;优先向纵向挖掘深入;递归过程
  • 得到第一个邻接节点的下标
//index表示从第几个点找邻接点
public int getFirstNeighbor(int index) {
    for (int i = 0; i < vertexList.size(); i++) {
        if (edges[index][i] == 1) {
            return i;
        }
    }
    return -1;
}
  • 根据前一个邻接节点的下标,获取下一个邻接节点
public int getNextNeighbor(int v1, int v2) {
    //从下标v2+1找v1的下一个邻接点
    for (int i = v2 + 1; i < vertexList.size(); i++) {
        if (edges[v1][i] == 1) {
            return i;
        }
    }
    return -1;
}
  • 深度优先遍历算法
private void dfs(boolean[] isVisited, int i) {
    System.out.print(getValueByIndex(i) + " -> ");
    //将这个节点设置为已经访问的节点
    isVisited[i] = true;
    //查找i的第一个邻接点
    int w = getFirstNeighbor(i);
    while (w != -1) {
        //说明存在邻接点,接着进入这个邻接点进行深入扫描;深度遍历的关键
        if (!isVisited[w]) {
            dfs(isVisited, w);
        }
        //对于第i个节点开始,w的下一个邻接点
        //对于一个点进行深入挖掘的关键,当进入一个点时,会将这个点的所有的点都遍历一遍
        //找到下一个没有被输出的邻接点,并再次深入这个新找的邻接点重复这个过程,直到扫描完毕
        w = getNextNeighbor(i,w);
    }
}
  • 对dfs进行重载,遍历所有节点,进行dfs;输出的点不在输出,如果是最后一个点,由于是双向的,那么他一定被前面的点扫描输出过了;如果是单独的一个点,也会被扫描到
public void dfs() {
    for (int i = 0; i < getNumOfVertex(); i++) {
        //如果被输出过,就跳过这个节点
        if (!isVisited[i]) {
            dfs(isVisited, i);
        }
    }
}

图的广度优先遍历

  • 横向,一层一层
  • Broad First Search,使用一个队列保持访问过的节点的顺序,按这个顺序来访问这些节点的邻接节点
  • 先把一个点的所有连通点找到
  • 广度优先遍历
public void bfs(boolean[] isVisited, int i) {
    int u; //队列头节点
    int w; //邻接节点
    LinkedList<Integer> queue = new LinkedList<>();
    //输出节点
    System.out.print(getValueByIndex(i) + " -> ");
    isVisited[i] = true;
    //将节点加入队列
    queue.addLast(i);
    while (!queue.isEmpty()) {
        //取出第一个
        u = queue.removeFirst();
        //得到第一个邻接点的下标
        w = getFirstNeighbor(u);
        while (w != -1) {
            if (!isVisited[w]) {
                System.out.print(getValueByIndex(w) + " -> ");
                //标记
                isVisited[w] = true;
                //加入队列,将u的所有临界点依次加入队列,并等u点全部扫描完毕后
                //按照队列的形式先进先出,依次将队列里的值作为新的u点扫描
                queue.addLast(w);
            }
            //以u为前驱点,找w后面的下一个邻接点
            //体现广度优先,现在一个u中进行扫描
            w = getNextNeighbor(u,w);
        }
     }
}
  • 遍历所有的节点,进行bfs
public void bfs() {
    for (int i = 0; i < getNumOfVertex(); i++) {
        if (!isVisited[i]) {
            bfs(isVisited,i);
        }
    }
}