Implementation and traversal of BFS and DFS

8.2. Implementation of summary chart

1. What is a graph?

  • node
  • side

2. How to construct edges and nodes?

  • Edges and nodes are definitely internal classes of Graph
  • Node: contains the value and the edge that ends with it, and the edge that starts with it
  • Edge: The edge may have a weight, that is, the weight in the above figure must include the vertex (to) and the starting vertex (from)

Edge and vertex creation

   //顶点内部类
    private static class Vertex<V, E> {
        //节点存储的值
        V value;
        //从节点出去的边,用set来存储,去重
        Set<Edge<V, E>> outEdges = new HashSet<>();
        //指向节点的边
        Set<Edge<V, E>> inEdges = new HashSet<>();

        public Vertex(V value) {
            this.value = value;
        }

        @Override
        public boolean equals(Object o) {
            return Objects.equals(value, ((Vertex<V, E>)o).value);
        }

        @Override
        public int hashCode() {
            return value == null ? 0 : value.hashCode();
        }

        @Override
        public String toString() {
            return value == null ? "null" : value.toString();
        }
    }

    //边
    private static class Edge<V, E> {
        Vertex<V, E> from;  //边的起点
        Vertex<V, E> to;    //边的终点
        E weight;       //边的权重

        public Edge(Vertex<V, E> from, Vertex<V, E> to) {   //构造方法
            this.from = from;
            this.to = to;
        }

        @Override
        public boolean equals(Object obj) {     //重写 equals和hashCode方法,因为边不能重复存储在哈希表中
//            if (this == o) return true;
//            if (this.getClass() != o) return false;
//            Edge<?, ?> edge = (Edge<?, ?>) o;
//            return from.equals(edge.from) &&
//                    to.equals(edge.to);
            if (this == obj) return true;
            if (this.getClass() != obj.getClass()) return false;
            Edge<V, E> edge = (Edge<V, E>) obj;
            return Objects.equals(from, edge.from) && Objects.equals(to, edge.to);
        }

        @Override
        public int hashCode() {
            return from.hashCode() * 31 + to.hashCode();
        }

        @Override
        public String toString() {  //toSting方法,from会调用它自己的toString
            return "Edge{" +
                    "from=" + from +
                    ", to=" + to +
                    ", weight=" + weight +
                    '}';
        }
    }

  • Fig
image-20200416170749410
  • In the graph interface, use map to store vertices and set to store edges
  //Map中的key,对应 它的顶点,用来存储顶点
private Map<V, Vertex<V, E>> vertices = new HashMap<>();
    //set用来存储边!
private Set<Edge<V, E>> edges = new HashSet<>();

1. Create edges and vertices

    @Override
    public void addVertex(V v) {
        if (vertices.get(v) != null) {
            return;
        }
        vertices.put(v, new Vertex<>(v));
    }

    @Override
    public void addEdge(V from, V to, E weight) {   //创建边要传递起点和终点
        Vertex<V, E> vertexFrom = vertices.get(from);   
        //如果顶点为空,则我们创建顶点
        if (vertexFrom == null) {
            vertexFrom = new Vertex<>(from);
            vertices.put(from, vertexFrom);
        }
        Vertex<V, E> vertexTo = vertices.get(to);
        if (vertexTo == null) {
            vertexTo = new Vertex<>(to);
            vertices.put(to, vertexTo);
        }
        Edge<V, E> edge = new Edge<>(vertexFrom, vertexTo);
        edge.weight = weight;
        //这里再起点和终点添加上这条边,先删除再添加!,因为更新了权值
        if (vertexFrom.outEdges.remove(edge)) {
            vertexTo.inEdges.remove(edge);
            edges.remove(edge);
        }
        //更新了weight,真正添加
        vertexFrom.outEdges.add(edge);
        vertexTo.inEdges.add(edge);
        edges.add(edge);
    }

    @Override
    public void addEdge(V from, V to) {
        addEdge(from, to, null);
    }

2. Delete edges and vertices

  • Delete edge
   @Override
    public void removeEdge(V from, V to) {
        //怎么删除边?边是怎么确定:from 和 to确定一条边,两点确定一条边
        //我们需要再from的outEdges中删了,和to的inEdges上了,还有总集合 edges上也删了!
        //Vertex<V, E> vertexFrom = vertices.get(from);
        Vertex<V, E> vertexFrom = vertices.get(from);
        if (vertexFrom == null) return;
        Vertex<V, E> vertexTo = vertices.get(to);
        if (vertexTo == null) return;
        //先创造出来再删除
        Edge<V, E> edge = new Edge<>(vertexFrom, vertexTo);
        if (vertexFrom.outEdges.remove(edge)) {
            vertexTo.inEdges.remove(edge);
            edges.remove(edge);
        }
    }

to sum up:

To delete the edges of the graph, delete the edges in three places:

  1. The starting point of OutEdges
  2. InEdges at the end
  3. The edges of the whole graph

And we used the method of creating first and then deleting when we deleted!

  • Delete vertex
 @Override
    public void removeVertex(V v) {
        //删除顶点,要把它包含的边都删了
        //怎么删? 遍历 OutEdges集合:删除它到达顶点的InEdges
        Vertex<V, E> vertex = vertices.remove(v);
        if (vertex == null)
            return;
        Set<Edge<V, E>> outEdges = vertex.outEdges;
        Set<Edge<V, E>> inEdges = vertex.inEdges;
        for (Iterator<Edge<V, E>> iterator = outEdges.iterator(); iterator.hasNext();) {
            Edge<V, E> edge = iterator.next();
            edge.to.inEdges.remove(edge);
            // 将当前遍历到的元素edge从集合vertex.outEdges中删掉
            iterator.remove();
            edges.remove(edge);
        }
        for (Iterator<Edge<V, E>> iterator = inEdges.iterator(); iterator.hasNext();) {
            Edge<V, E> edge = iterator.next();
            edge.from.outEdges.remove(edge);
            // 将当前遍历到的元素edge从集合vertex.outEdges中删掉
            iterator.remove();
            edges.remove(edge);
        }
    }

to sum up:To delete a vertex, delete all the edges it occupies, that is, delete the edges on its inEdges and OutEdges collections.

How to delete? InEdge, which traverses the end point of each edge of its outEdges, deletes this edge.

 Set<Edge<V, E>> outEdges = vertex.outEdges;
        Set<Edge<V, E>> inEdges = vertex.inEdges;
        for (Iterator<Edge<V, E>> iterator = outEdges.iterator(); iterator.hasNext();) {
            Edge<V, E> edge = iterator.next();
            edge.to.inEdges.remove(edge);
            // 将当前遍历到的元素edge从集合vertex.outEdges中删掉
            iterator.remove();
            edges.remove(edge);
        }
        for (Iterator<Edge<V, E>> iterator = inEdges.iterator(); iterator.hasNext();) {
            Edge<V, E> edge = iterator.next();
            edge.from.outEdges.remove(edge);
            // 将当前遍历到的元素edge从集合vertex.outEdges中删掉
            iterator.remove();
            edges.remove(edge);
        }

3. Traverse

  1. bfs : Breadth first search

Ideas:

It is very similar to the sequence traversal of binary tree, application queue, layer by layer traversal

Note that in the figure, the next layer refers to the position that this node can reach in one step

image-20200416202510101

But here, the outEdges that traverse the vertices are enqueued in sequence, but when the graph has a loop, the Si loop may repeatedly appear, so we use set to repeat!

  • Code
 //遍历图,使用DFS算法!

    public void bfs(V value) {
        Vertex<V, E> vertex = vertices.get(value);  //遍历的起点
        if (vertex == null) return;
        Queue<Vertex<V, E>> queue = new LinkedList<>();     //使用队列,java中队列由双向链表实现
        Set<Vertex<V, E>> set = new HashSet<>();        //用set去重,怎么去?入队的同时在set中加上
        queue.offer(vertex);
        set.add(vertex);
        while (!queue.isEmpty()) {
            Vertex<V, E> poll = queue.poll();
            System.out.println(poll.value);
            for (Edge<V, E> outEdge : poll.outEdges) {  //outEdges的终点就是下一层节点
                if (set.contains(outEdge.to)) continue;
                queue.offer(outEdge.to);
                set.add(outEdge.to);
            }
        }
    }
  1. DFS : depth frist search
  • Recursive implementation

Ideas:When we want to recurse, do n’t think about it deeply, it ’s actually very simple.

image-20200416204139165

  • Code:
//深度优先搜素,相当于前序遍历!
    @Override
    public void dfs(V begin) {
        Vertex<V, E> vertex = vertices.get(begin);
        if (vertex == null)
            return;
        dfs(vertex, new HashSet<>());
    }

    private void dfs(Vertex<V,E> vertex, Set<Vertex<V, E>> set) {
        if (vertex == null)
            return;
        System.out.println(vertex.value);
        set.add(vertex);
        for (Edge<V, E> outEdge : vertex.outEdges) {
            if (set.contains(outEdge.to)) continue;
            dfs(outEdge.to, set);
        }
    }

The use of set to deduplication is similar to the deduplication idea above!

to sum up: How to understand recursion in a loop:

image-20200416204409099

If it is the above picture, we start traversing from node 1: when it comes up, it will enter the for loop and traverse

dfs (3) 、 dfs (5) 、 dfs (2) 、 dfs (0)

Entering dfs (3) is a depth-first search, traversing 7, and traversing dfs (5) after traversing 3 ···

Just understand it!

3. DFS non-recursive implementation

All recursion can be transformed into iterations, but some are difficult

why?

Because recursion is the process of pushing the function JVM onto the stack, and the stack in our data structure also has this feature, so many complex recursions can be implemented using stacks and iterations!

Code: The main idea is to use the stack, combined with the advanced features of the stack for deep thinking!

 public void dfs(V begin, Visitor<V, E> visitor) {
        if (visitor == null) return;
        Vertex<V, E> vertex = vertices.get(begin);
        if (vertex == null) {
            return;
        }
        Stack<Vertex<V, E>> stack = new Stack<>();
        Set<Vertex<V, E>> visited = new HashSet<>();
        stack.push(vertex);
        if (visitor.visit(vertex.value)) return;
        visited.add(vertex);
        while (!stack.isEmpty()) {
            Vertex<V, E> pop = stack.pop();
            for(Edge<V, E> edge : pop.outEdges) {
                if (visited.contains(edge.to)) continue;
                stack.push(pop);
                stack.push(edge.to);
                if (visitor.visit(edge.to.value)) return;
                visited.add(edge.to);
                break;
            }
        }
    }


to sum up: Set is unordered, so I do n’t know why to traverse that edge first, why both from and to are put in Break, we only access the value of to node each time

Guess you like

Origin www.cnblogs.com/bingstudy/p/12728734.html