高级数据结构之图

1.图概念

  1. 顶点(vertex):图的元素我们就叫做顶点(vertex)
  2. 边(edge):一个顶点可以与任意其他顶点建立连接关系,这种建立的关系叫做边(edge)
  3. 度(degree):顶点相连接的边的条数叫做度(degree)
  4. 有向图:边有方向的图叫做有向图
  5. 无向图:边无方向的图叫无向图
  6. 带权图:每条边都有一个权重(weight),可以通过这个权重来表示 一些可度量的值 ,这样的图叫做带权图(weighted graph)

2.图的存储

存储可以采用数组或链表,数组采用邻接矩阵,链表采用邻接表

2.1 邻接矩阵存储

邻接矩阵(Adjacency Matrix)的底层是一个二维数组graph[row][col],row表示行,col表示列
优点:快,可以通过下标访问
缺点:浪费空间,用二维数组实现确开辟了多余空间维护与其他顶点的关系

2.1.1 无向图


行列下标0-5表示A-F【也可以行列下标1-6表示A-F,下标从[1][1]开始,直观但是浪费了一行一列空间】
如graph[1][4]=graph[B][E]=0表示B到E没边
如graph[1][3]=graph[B][D]=1表示B到D有边
分析:B实际只与A和D有关系,用二维数组实现确开辟了多余空间维护与其他顶点的关系

2.1.2 有向图


graph[0][1]=1表示1指向2,graph[1][0]=0表示2没指向1
graph[1][2]=1,graph[2][1]=1,表示23之间相互指向

2.1.3 带权图


数组中就存储相应的权重

2.1.4 代码实现

package datastructure.graph;

import java.util.*;

/**
 * 图
 *
 * @author zw
 * @create 2023-04-17 0:22
 */
public class Graph<T> {
    // 是否有方向,默认无,有向图顶点关系都双向指向时与无向图没发区分,需要通过属性区分
    private boolean hasDirection = false;
    private Map<T, Integer> edgesMap; // 保存顶点与矩阵下标关系
    private Map<Integer, T> indexEdgesMap; // 保存顶点与矩阵下标关系
    private List<T> vertexList;//存储点的链表
    private int[][] edges;//邻接矩阵,用来存储边,值是权值
    private int numOfEdges;//边的数目

    public Graph(int n, boolean hasDirection) {
        this(n);
        this.hasDirection = hasDirection;
    }

    public Graph(int n) {
        //初始化矩阵,一维数组,和边的数目
        edges = new int[n][n];
        vertexList = new ArrayList(n);
        edgesMap = new HashMap<>();
        indexEdgesMap = new HashMap<>();
        numOfEdges = 0;
    }

    //得到结点的个数
    public int getNumOfVertex() {
        return vertexList.size();
    }

    //得到边的数目
    public int getNumOfEdges() {
        return numOfEdges;
    }

    //返回结点i的数据
    public Object getValueByIndex(int i) {
        return vertexList.get(i);
    }

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

    //返回v1,v2的权值
    public int getWeight(T v1, T v2) {
        return edges[edgesMap.get(v1)][edgesMap.get(v2)];
    }

    //插入结点
    public void insertVertex(T vertex) {
        vertexList.add(vertex);
        Integer max = edgesMap.values().stream().max(Comparator.comparing(x -> x)).orElse(-1);
        edgesMap.put(vertex, max + 1);
    }

    //删除结点
    public void deleteVertex(T vertex) {
        int size = vertexList.size();
        vertexList.remove(vertex);
        Integer vertexIndex = edgesMap.get(vertex);
        edgesMap.remove(vertex);
        // 该点行列全变为0,回收所有边
        for (int col = 0; col < size; col++) {
            edges[vertexIndex][col] = 0;
        }
        for (int row = 0; row < size; row++) {
            edges[row][vertexIndex] = 0;
        }
    }

    //插入带权边
    public void insertEdge(int v1, int v2, int weight) {
        edges[v1][v2] = weight; // 有方向
        if (!hasDirection) {
            edges[v2][v1] = weight; // 无方向
        }
        numOfEdges++;
    }

    //插入带权边
    public void insertEdge(T v1, T v2, int weight) {
        edges[edgesMap.get(v1)][edgesMap.get(v2)] = weight; // 有方向
        if (!hasDirection) {
            edges[edgesMap.get(v2)][edgesMap.get(v1)] = weight; // 无方向
        }
        numOfEdges++;
    }

    //插入不带权边
    public void insertEdge(int v1, int v2) {
        edges[v1][v2] = 1;
        if (!hasDirection) {
            edges[v2][v1] = 1; // 无方向
        }
        numOfEdges++;
    }

    //插入不带权边
    public void insertEdge(T v1, T v2) {
        edges[edgesMap.get(v1)][edgesMap.get(v2)] = 1; // 有方向
        if (!hasDirection) {
            edges[edgesMap.get(v2)][edgesMap.get(v1)] = 1; // 无方向
        }
        numOfEdges++;
    }


    //删除边
    public void deleteEdge(int v1, int v2) {
        edges[v1][v2] = 0;
        if (!hasDirection) {
            edges[v2][v1] = 0; // 无方向
        }
        numOfEdges--;
    }

    //删除带权边
    public void deleteEdge(T v1, T v2) {
        edges[edgesMap.get(v1)][edgesMap.get(v2)] = 0; // 有方向
        if (!hasDirection) {
            edges[edgesMap.get(v2)][edgesMap.get(v1)] = 0; // 无方向
        }
        numOfEdges--;
    }
}

测试用例

    private void print() {
        for (int[] rows : edges) {
            System.out.println(Arrays.toString(rows));
        }
    }

    public static void main(String[] args) {
        String[] nodes = {"A", "B", "C", "D", "E", "F"};
        String[][] weightedEdges = {
                {"A", "B","2"}, {"A", "C","2"}, {"A", "D","2"},
                {"B", "D","3"},
                {"C", "E","4"}, {"C", "F","4"},
                {"D", "E","5"}, {"D", "F","5"},
                {"E", "F","6"},};
        System.out.println("---------无向图------------");
        Graph<String> graph = new Graph<String>(6);
        for (String node : nodes) {
            graph.insertVertex(node);
        }
        for (String[] edge : weightedEdges) {
            graph.insertEdge(edge[0], edge[1]);
        }
        graph.print();
        System.out.println("---------有向图------------");
        Graph<String> directedGraph = new Graph<String>(6,true);
        for (String node : nodes) {
            directedGraph.insertVertex(node);
        }
        for (String[] edge : weightedEdges) {
            directedGraph.insertEdge(edge[0], edge[1]);
        }
        directedGraph.print();
        System.out.println("---------带权有向图------------");
        Graph<String> weightedGraph = new Graph<String>(6,true);
        for (String node : nodes) {
            weightedGraph.insertVertex(node);
        }
        for (String[] edge : weightedEdges) {
            weightedGraph.insertEdge(edge[0], edge[1],Integer.valueOf(edge[2]));
        }
        weightedGraph.print();
    }

运行结果

---------无向图------------
[0, 1, 1, 1, 0, 0]
[1, 0, 0, 1, 0, 0]
[1, 0, 0, 0, 1, 1]
[1, 1, 0, 0, 1, 1]
[0, 0, 1, 1, 0, 1]
[0, 0, 1, 1, 1, 0]
---------有向图------------
[0, 1, 1, 1, 0, 0]
[0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 1]
[0, 0, 0, 0, 1, 1]
[0, 0, 0, 0, 0, 1]
[0, 0, 0, 0, 0, 0]
---------带权有向图------------
[0, 2, 2, 2, 0, 0]
[0, 0, 0, 3, 0, 0]
[0, 0, 0, 0, 4, 4]
[0, 0, 0, 0, 5, 5]
[0, 0, 0, 0, 0, 6]
[0, 0, 0, 0, 0, 0]

2.2 邻接表存储

优点:节省空间
缺点:慢

稀疏图:顶点很多,但每个顶点的边并不多。邻接矩阵的存储方法就更加浪费空间,邻接表解决

数组+链表进行存储,数组中元素表示出发的点,链表表示能到的所有点
image.png

2.2.1 代码实现

顶点

class Vertex<T> {
    String name;//顶点唯一标识
    T data; // 携带数据
    boolean visit; // 是否访问过,默认false
    Edge next;//从该定点出发的边

    public Vertex(String name, Edge edge, T data) {
        this.name = name;
        this.next = edge;
        this.data = data;
    }

    @Override
    public String toString() {
        return name;
    }
}

class Edge {
    String name;//指向下一个顶点
    int weight;//权重
    boolean visit; // 是否访问过,默认false
    Edge next;//指向的下一个边

    public Edge(String name, int weight, Edge next) {
        this.name = name;
        this.weight = weight;
        this.next = next;
    }
}

public class Graph<T> {
    // 存储:数组+链表 也就是map
    Map<String, Vertex> vertexsMap = new HashMap<>();

    /**
     * 添加顶点
     *
     * @param name 顶点名称
     */
    public void insertVertex(String name) {
        Vertex vertex = new Vertex(name, null, null);
        vertexsMap.put(name, vertex);
    }

    public void insertVertex(String name, T data) {
        Vertex vertex = new Vertex(name, null, data);
        vertexsMap.put(name, vertex);
    }

    /**
     * 删除顶点
     *
     * @param deleteVertexName 顶点名称
     */
    public void deleteVertex(String deleteVertexName) {
        vertexsMap.remove(deleteVertexName);
        // 删除其他顶点到删除顶点的边
        vertexsMap.forEach((startVertexName, vertex) -> {
            Edge currEdge = vertex.next;
            if (currEdge != null && currEdge.name.equals(deleteVertexName)) {
                vertex.next = currEdge.next;
            } else {
                while (currEdge != null) {
                    Edge next = currEdge.next;
                    if (next != null && next.name.equals(deleteVertexName)) {
                        currEdge.next = next.next;
                        break;
                    }
                    currEdge = currEdge.next;
                }
            }
        });
    }

    /**
     * 插入边
     *
     * @param start  开始顶点名称
     * @param end    结束顶点名称
     * @param weight 权重
     */
    public void insertEdge(String start, String end, int weight) throws Exception {
        //现获取开始顶点
        Vertex startVertex = vertexsMap.get(start);
        Vertex endVertex = vertexsMap.get(end);
        if (startVertex == null || endVertex == null) {
            throw new Exception("起止顶点可能不存在");
        }
        Edge edge = new Edge(end, weight, null);
        if (startVertex.next == null) {
            startVertex.next = edge;
        } else {
            //如果不为null 寻找节点的next==null的位置,挂上这个边
            Edge lastEdge = startVertex.next;
            while (lastEdge.next != null) {
                lastEdge = lastEdge.next;
            }
            lastEdge.next = edge;
        }
    }


    /**
     * 删除边
     *
     * @param startVertexName 起始顶点名
     * @param endVertexName   结束顶点名
     * @throws Exception
     */
    public void deleteEdge(String startVertexName, String endVertexName) throws Exception {
        Vertex vertex = vertexsMap.get(startVertexName);
        if (vertex == null) {
            throw new Exception(startVertexName + "顶点不存在");
        }
        Edge currEdge = vertex.next;
        if (currEdge.name.equals(endVertexName)) {
            vertex.next = currEdge.next;
        } else {
            while (currEdge != null) {
                Edge next = currEdge.next;
                if (next != null && next.name.equals(endVertexName)) {
                    currEdge.next = next.next;
                    break;
                }
                currEdge = currEdge.next;
            }
        }

    }

    public void print() {
        vertexsMap.forEach((vertexName, vertex) -> {
            Edge edge = vertex.next;
            while (edge != null) {
                System.out.println(vertexName + "指向" + edge.name + " 权重" + edge.weight);
                edge = edge.next;
            }
        });
    }


}

测试用例

    public static void main(String[] args) throws Exception {
        Graph graph = new Graph();
        String[] vertexs = {"A", "B", "C", "D", "E", "F"};
        for (String vertex : vertexs) {
            graph.insertVertex(vertex);
        }
        String[][] weightedEdges = {
                {"C", "A", "1"},
                {"F", "C", "2"},
                {"A", "B", "4"},
                {"E", "B", "2"},
                {"A", "D", "5"},
                {"D", "F", "4"},
                {"D", "E", "3"}};
        for (String[] weightedEdge : weightedEdges) {
            graph.insertEdge(weightedEdge[0], weightedEdge[1], Integer.valueOf(weightedEdge[2]));
        }
        graph.print();

        System.out.println("-----删除C-----");
        graph.deleteVertex("C");
        graph.print();

        System.out.println("-----删除A到B方向的边-----");
        graph.deleteEdge("A", "B");
        graph.print();
    }

运行结果

A指向B 权重4
A指向D 权重5
C指向A 权重1
D指向F 权重4
D指向E 权重3
E指向B 权重2
F指向C 权重2
-----删除C-----
A指向B 权重4
A指向D 权重5
D指向F 权重4
D指向E 权重3
E指向B 权重2
-----删除AC方向的边-----
A指向D 权重5
D指向F 权重4
D指向E 权重3
E指向B 权重2

3. 图的遍历

  1. 从指定的顶点(称为初始点)出发
  2. 按照某种搜索方法沿着图的边访问图中的所有顶点,每个顶点仅被访问一次
  3. 遍历过程中得到的顶点序列称为图遍历序列

图的遍历策略:

  1. 深度优先搜索 (DFS,Depth First Search )

从起点出发,从规定的方向中选择其中一个不断地向前走,直到无法继续为止,然后回溯尝试另外一种方向,直到最后走到终点。就像走迷宫一样,尽量往深处走

DFS 解决的是连通性的问题

即,给定两个点,一个是起始点,一个是终点,判断是不是有一条路径能从起点连接到终点。起点和终点,也可以指的是某种起始状态和最终的状态。问题的要求并不在乎路径是长还是短,只在乎有还是没有。

  1. 广度优先搜索(BFS,Breadth First Search )

3.1 广度优先遍历

3.1.1 邻接矩阵广度优先遍历

  1. 将起始顶点放入队列
  2. 出队并记录
  3. 获取该顶点能到的所有顶点(从小到大排序),放入队列
  4. 循环2、3步骤,直到队列没数据

从指定顶点广度优先遍历

  /**
     * 从指定点广度优先遍历
     *
     * @param vertex    起始顶点
     * @param mark      记录边是否走过
     * @param vertexMap 记录顶点是否走过
     * @return
     */
    private String bfs(T vertex, boolean[][] mark, Map<T, Boolean> vertexMap) {
        if (mark == null) {
            mark = new boolean[edges.length][edges.length];
        }
        if (vertexMap == null) {
            vertexMap = vertexList.stream().collect(Collectors.toMap(item -> item, item -> Boolean.FALSE));
        }
        StringBuffer sb = new StringBuffer();
        LinkedListQueue<T> queue = new LinkedListQueue<T>(16);
        queue.add(vertex);
        while (!queue.isEmpty()) {
            T poll = queue.poll();
            sb.append(poll).append(" ");
            Integer edgeIndex = edgesMap.get(poll);
            // 获取该顶点指向的顶点
            for (int col = 0; col < edges.length; col++) { // 这就是顺序遍历了
                int value = edges[edgeIndex][col];
                T toVertex = indexEdgesMap.get(col);
                if (value > 0 && !mark[edgeIndex][col] && !vertexMap.get(toVertex)) {
                    vertexMap.put(toVertex, true);
                    mark[edgeIndex][col] = true; // 标记访问记录
                    if (!hasDirection) {
                        mark[col][edgeIndex] = true; //无向图标记量变
                    }
                    queue.add(toVertex);
                }
            }
        }
        return sb.toString();
    }

测试用例

public static void main(String[] args) {
        String[] vertexs = {"A", "B", "C", "D", "E", "F", "G", "H"};
        String[][] edges = {
                {"A", "B", "2"}, {"A", "D", "2"}, {"A", "G", "2"},
                {"B", "E", "3"}, {"B", "F", "3"},
                {"C", "F", "4"}, {"C", "H", "4"},
                {"D", "A", "5"}, {"D", "F", "5"},
                {"E", "B", "6"}, {"E", "G", "6"},
                {"F", "B", "6"}, {"F", "D", "6"}, {"F", "C", "6"},
                {"G", "A", "6"}, {"G", "E", "6"},
                {"H", "C", "6"},

        };
        Graph<String> dfsGraph = new Graph<String>(8);
        for (String node : vertexs) {
            dfsGraph.insertVertex(node);
        }
        for (String[] edge : edges) {
            dfsGraph.insertEdge(edge[0], edge[1]);
        }
        dfsGraph.print();
     	System.out.println("---------广度优先遍历------------");
        System.out.println(dfsGraph.bfs("A", null, null));
}

运行结果

[0, 1, 0, 1, 0, 0, 1, 0]
[1, 0, 0, 0, 1, 1, 0, 0]
[0, 0, 0, 0, 0, 1, 0, 1]
[1, 0, 0, 0, 0, 1, 0, 0]
[0, 1, 0, 0, 0, 0, 1, 0]
[0, 1, 1, 1, 0, 0, 0, 0]
[1, 0, 0, 0, 1, 0, 0, 0]
[0, 0, 1, 0, 0, 0, 0, 0]
---------广度优先遍历------------
A B D G E F C H 

3.1.2 邻接表广度优先遍历

    /**
     * 广度优先遍历
     *
     * @param startVertexName 搜索起始位置
     */
    private void bfs(String startVertexName) {
        MyPriorityQueue<Vertex> priorityQueue = new MyPriorityQueue<Vertex>((o1, o2) -> o1.name.compareTo(o2.name));
        LinkedListQueue<Vertex> queue = new LinkedListQueue<Vertex>(16); // 辅助队列
        // 获取顶点
        Vertex vertex = vertexsMap.get(startVertexName);
        vertex.visit = true; // 标记顶点
        queue.add(vertex); // 放入队列中
        while (!queue.isEmpty()) {
            Vertex currVertex = queue.poll();
            System.out.print(currVertex.name + " ");
            Edge currEdge = currVertex.next;
            // 将当前顶点指向的订单放入队列
            while (currEdge != null && !currEdge.visit) {
                currEdge.visit = true; // 记录访问
                Vertex nextVertex = vertexsMap.get(currEdge.name);
                priorityQueue.offer(nextVertex);
                currEdge = currEdge.next;
            }
            while (!priorityQueue.isEmpty()) {
                Vertex poll = priorityQueue.poll();
                if (!poll.visit) {
                    poll.visit = true; // 标记顶点
                    queue.add(poll);
                }
            }
        }
    }

测试用例

    public static void main(String[] args) throws Exception {
        AdjacencyList bfsGraph = new AdjacencyList();
        vertexs = new String[]{"A", "B", "C", "D", "E", "F", "G", "H"};
        weightedEdges = new String[][]{
                {"A", "B", "2"}, {"A", "D", "2"}, {"A", "G", "2"},
                {"B", "E", "3"}, {"B", "F", "3"},
                {"C", "F", "4"}, {"C", "H", "4"},
                {"D", "A", "5"}, {"D", "F", "5"},
                {"E", "B", "6"}, {"E", "G", "6"},
                {"F", "B", "6"}, {"F", "D", "6"}, {"F", "C", "6"},
                {"G", "A", "6"}, {"G", "E", "6"},
                {"H", "C", "6"}
        };

        for (String vertex : vertexs) {
            bfsGraph.insertVertex(vertex);
        }
        for (String[] weightedEdge : weightedEdges) {
            bfsGraph.insertEdge(weightedEdge[0], weightedEdge[1], Integer.valueOf(weightedEdge[2]));
        }
        System.out.println("广度优先遍历");
        bfsGraph.bfs("A");
    }

运行结果

广度优先遍历
A B D G E F C H 

3.2 深度优先遍历

3.2.1 邻接表深度优先遍历

    /**
     * 深度优先遍历,思路:
     * 1、处理起始顶,放入栈中
     * 2、获取下一个未访问的顶,放入栈中
     * 3、获取下一个未访问的顶为null说明访问到最深处
     * 4、此时弹栈,循环2和3
     *
     * @param startVertexName 起始顶点
     */
    private String dfs(String startVertexName) {
        StringBuffer sb = new StringBuffer();
        LinkedListStack<Vertex> stack = new LinkedListStack<>();// 辅助栈
        Vertex vertex = vertexsMap.get(startVertexName); // 获取顶点

        stack.push(vertex); // 放入栈中
        vertex.visit = true; // 标记顶点
        sb.append(vertex.name).append(" ");

        Edge currEdge = vertex.next;
        Vertex currVertex = currEdge == null ? null : vertexsMap.get(currEdge.name);
        // 深度优先遍历
        while (currVertex != null) {
            if (!currVertex.visit) {
                currVertex.visit = true;
                sb.append(currVertex.name).append(" ");
                stack.push(currVertex);
                System.out.println("当前遍历顺序:"+sb.toString());
                System.out.println("当前辅助栈:"+stack.toString());

                currVertex = getFirstNoVisitVertex(currVertex);
                while (currVertex == null) { // 深度到头了,需要回溯
                    Vertex visitVertex = stack.pop();
                    System.out.println("弹栈:"+stack.toString());
                    if (visitVertex == null) break; // 已访问顶点全部回溯了
                    currVertex = getFirstNoVisitVertex(visitVertex);
                }
            }
        }
        return sb.toString();
    }

    /**
     * 获取下个未访问的顶点
     *
     * @param vertex
     * @return
     */
    private Vertex getFirstNoVisitVertex(Vertex vertex) {
        // 辅助优先队列,确定顺序,用于验证结果
        MyPriorityQueue<Vertex> priorityQueue = new MyPriorityQueue<Vertex>((o1, o2) -> o1.name.compareTo(o2.name));
        if (vertex == null) return null;
        Edge edge = vertex.next;
        while (edge != null) {
            Vertex nextVertex = vertexsMap.get(edge.name);
            if (!nextVertex.visit) {
                priorityQueue.offer(nextVertex);
                //return nextVertex;
            }
            edge = edge.next;
        }
        if (!priorityQueue.isEmpty()) return priorityQueue.poll();// 弹出排序后的
        return null;
    }

测试用例

 AdjacencyList bfsGraph = new AdjacencyList();
        vertexs = new String[]{"A", "B", "C", "D", "E", "F", "G", "H"};
        weightedEdges = new String[][]{
                {"A", "B", "2"}, {"A", "D", "2"}, {"A", "G", "2"},
                {"B", "E", "3"}, {"B", "F", "3"},
                {"C", "F", "4"}, {"C", "H", "4"},
                {"D", "A", "5"}, {"D", "F", "5"},
                {"E", "B", "6"}, {"E", "G", "6"},
                {"F", "B", "6"}, {"F", "D", "6"}, {"F", "C", "6"},
                {"G", "A", "6"}, {"G", "E", "6"},
                {"H", "C", "6"}
        };
        for (String vertex : vertexs) {
            bfsGraph.insertVertex(vertex);
        }
        for (String[] weightedEdge : weightedEdges) {
            bfsGraph.insertEdge(weightedEdge[0], weightedEdge[1], Integer.valueOf(weightedEdge[2]));
        }
 		String dfsResult = bfsGraph.dfs("A");
        System.out.println("深度优先遍历:"+dfsResult);

运行结果

当前遍历顺序:A B 
当前辅助栈:[B,A]
当前遍历顺序:A B E 
当前辅助栈:[E,B,A]
当前遍历顺序:A B E G 
当前辅助栈:[G,E,B,A]
弹栈:[E,B,A]
弹栈:[B,A]
弹栈:[A]
当前遍历顺序:A B E G F 
当前辅助栈:[F,A]
当前遍历顺序:A B E G F C 
当前辅助栈:[C,F,A]
当前遍历顺序:A B E G F C H 
当前辅助栈:[H,C,F,A]
弹栈:[C,F,A]
弹栈:[F,A]
弹栈:[A]
当前遍历顺序:A B E G F C H D 
当前辅助栈:[D,A]
弹栈:[A]
弹栈:[]
弹栈:[]
深度优先遍历:A B E G F C H D 

3.2.2 邻接矩阵深度优先遍历

    /**
     * 深度优先遍历
     */
    public void dfs_cycle() {
        Map<T, Boolean> vertexMap = vertexList.stream().collect(Collectors.toMap(item -> item, item -> Boolean.FALSE));
        //    遍历所有的节点,进行dfs
        for (int row = 0; row < getNumOfVertex(); row++) {
            T vertex = indexEdgesMap.get(row);
            if (!vertexMap.get(vertex)) {
                dfs_cycle(vertex, vertexMap);
            }
        }
    }

    /**
     * 深度优先遍历
     *
     * @param vertex
     * @param vertexMap
     */
    private void dfs_cycle(T vertex, Map<T, Boolean> vertexMap){
        //    输出访问节点
        System.out.print(vertex + " ");
        //    将该节点设置为已经访问
        vertexMap.put(vertex,true);
        //    查找节点i的第一个领结节点w
        T toVertex = getFirstNextXertex(vertex);
        //如果存在邻接节点
        while (toVertex != null) {
            //如果这个节点没有被访问;
            if (!vertexMap.get(toVertex)) {
                dfs_cycle(toVertex, vertexMap);
            }
            //    如果w节点应景访问了
            toVertex = getNextXertex(vertex, toVertex);
        }
    }

    /**
     * 查找第一个连通的顶点
     * @param vertex
     * @return
     */
    private T getFirstNextXertex(T vertex){
        Integer edgeIndex = edgesMap.get(vertex);
        for (int col = 0; col < edges.length; col++) {
            int value = edges[edgeIndex][col];
            T toVertex = indexEdgesMap.get(col);
            if(value>0) return toVertex;
        }
        return null;
    }

    /**
     *
     * @param vertex 开始顶点
     * @param findStartVertex 查找起始位置
     * @return 下个顶点下标
     */
    private T getNextXertex(T vertex,T findStartVertex){
        Integer row = edgesMap.get(vertex);
        Integer indStartCol = edgesMap.get(findStartVertex);
        for (int col = indStartCol+1; col < edges.length; col++) {
            int value = edges[row][col];
            T toVertex = indexEdgesMap.get(col);
            if(value>0) return toVertex;
        }
        return null;
    }

测试用例

public static void main(String[] args) {
        System.out.println("---------深度优先遍历------------");
        String[] vertexs = {"A", "B", "C", "D", "E", "F", "G", "H"};
        String[][] edges = {
                {"A", "B", "2"}, {"A", "D", "2"}, {"A", "G", "2"},
                {"B", "E", "3"}, {"B", "F", "3"},
                {"C", "F", "4"}, {"C", "H", "4"},
                {"D", "A", "5"}, {"D", "F", "5"},
                {"E", "B", "6"}, {"E", "G", "6"},
                {"F", "B", "6"}, {"F", "D", "6"}, {"F", "C", "6"},
                {"G", "A", "6"}, {"G", "E", "6"},
                {"H", "C", "6"},

        };
        Graph<String> dfsGraph = new Graph<String>(8);
        for (String node : vertexs) {
            dfsGraph.insertVertex(node);
        }
        for (String[] edge : edges) {
            dfsGraph.insertEdge(edge[0], edge[1]);
        }
        dfsGraph.print();
        dfsGraph.dfs_cycle();
}

运行结果

---------深度优先遍历------------
[0, 1, 0, 1, 0, 0, 1, 0]
[1, 0, 0, 0, 1, 1, 0, 0]
[0, 0, 0, 0, 0, 1, 0, 1]
[1, 0, 0, 0, 0, 1, 0, 0]
[0, 1, 0, 0, 0, 0, 1, 0]
[0, 1, 1, 1, 0, 0, 0, 0]
[1, 0, 0, 0, 1, 0, 0, 0]
[0, 0, 1, 0, 0, 0, 0, 0]
A B E G F C H D 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值