【数据结构Java】--图、BFS、DFS、拓扑结构

news2024/9/20 11:35:51

目录

一、图(Graph)

1.概念

2.有向图

 3.出度、入度

4.无向图

5.简单图、多重图

6.无向完全图

 7.有向完全图

8.有权图

9.连通图

 10.连通分量(无向图)

 11.强连通图(有向图)

12.强连通分量

 13.邻接矩阵

1.基本概念

2.邻接矩阵--有权图

14.邻接表

 1.基本概念 

 2.有权图

15.图的实现

图的基础接口

 添加边addEdge

图的总边(edgesSize)

图的点的接口

图的边的接口

删除边removeEdge

完整代码

二、图的遍历

新的图接口

三、广度优先搜索(BFS)

1.图解

​编辑 2.思路

3.代码实现

四、深度优先搜索(DFS)

1.前序遍历

 2.图解

3.递归实现

 4.通过栈-----非递归实现

1)步骤

2)图解

 3)代码实现

五、AOV网(Activity On Vertex Network)

六、拓扑排序(Topological Sort) 

1.简介

 2.思路

3.代码实现

一、图(Graph)

1.概念

2.有向图

 3.出度、入度

4.无向图

 

5.简单图、多重图

6.无向完全图

 7.有向完全图

8.有权图

 

9.连通图

 10.连通分量(无向图)

 11.强连通图(有向图)

12.强连通分量

 13.邻接矩阵

1.基本概念

2.邻接矩阵--有权图

 

14.邻接表

 1.基本概念 

 2.有权图

15.图的实现

图的基础接口

package graph;

public interface Graph<V,E> {
//    边的数量
    int edgesSize();
//    顶点数量
    int verticesSize();

//    添加顶点
    void addVertex(V v);
//    添加边(无权值)
    void addEdge(V from,V to);
//    添加边(有权值)
    void addEdge(V from,V to,E weight);

//    删除顶点
    void removeVertex(V v);
//    删除边
    void removeEdge(V from,V to);

    interface vertexVisitor<V>{
        boolean visit(V v);
    }
    
    void print();
}

 添加边addEdge

    /**
     * 添加无权值的边
     * @param from
     * @param to
     */
    @Override
    public void addEdge(V from, V to) {
        addEdge(from,to,null);
    }

    /**
     * 添加有权值的边
     * @param from
     * @param to
     * @param weight
     */
    @Override
    public void addEdge(V from, V to, E weight) {
        //根据传入的参数from找到出发点,如果不存在则创建
        Vertex<V,E> fromVertex=vertices.get(from);
        if (fromVertex==null){
            fromVertex=new Vertex<>(from);
            //将点和对应的点关系存入
            vertices.put(from,fromVertex);
        }
        //根据传入参数to找到终点,如果找不到则创建
        Vertex<V,E> toVertex=vertices.get(to);
        if (toVertex==null){
            toVertex=new Vertex<>(to);
            //将点和对应的点关系存入
            vertices.put(to,toVertex);
        }
        //根据出发点和终点,创建边
        Edge<V,E> edge=new Edge<>(fromVertex,toVertex);
        edge.weight=weight;//有权值加上,无权值则为null

        //不管原来是否存在,都先删除,在添加进去
        if (fromVertex.outEdges.contains(edge)){ //说明存在
            toVertex.inEdges.remove(edge);
            //在整个图中的边减少
            edges.remove(edge);
        }
        fromVertex.outEdges.add(edge);
        toVertex.inEdges.add(edge);
        //在整个图中的边增加
        edges.add(edge);

    }

图的总边(edgesSize)

    public int edgesSize() {
        return edges.size();
    }

图的点的接口

    /**
     * 顶点
     * @param <V>
     * @param <E>
     */
    private static class Vertex<V,E>{
        //顶点值
        V value;
        Set<Edge<V,E>> inEdges=new HashSet<>();
        Set<Edge<V,E>> outEdges=new HashSet<>();

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

        /**
         * 比较两个顶点是否相等
         * @param obj
         * @return
         */
        @Override
        public boolean equals(Object obj) {
            return Objects.equals(value,((Vertex<V,E>)obj).value);
        }

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

图的边的接口

    /**
     * 边
     * @param <V>
     * @param <E>
     */
    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;
        }

        /**
         * 判断两条边是否相同
         * @param obj
         * @return
         */
        @Override
        public boolean equals(Object obj) {
            Edge<V,E> edge=(Edge<V, E>) obj;
            return Objects.equals(from,edge.from) && Objects.equals(to,edge.to);
        }

        /**
         * 如果equals的结果是true说明一样,则hashcode值也一样
         * @return
         */
        @Override
        public int hashCode() {
            return from.hashCode()+to.hashCode();
        }
    }

删除边removeEdge

    /**
     * 删除边
     * @param from
     * @param to
     */
    @Override
    public void removeEdge(V from, V to) {
//        根据传入的from获得起点,不存在则不需要删除
        Vertex<V,E> fromVertex=vertices.get(from);
        if (fromVertex ==null){
            return;
        }
//        根据传入的to找到终点,不存在则不需要删除
        Vertex<V,E> toVertex=vertices.get(to);
        if (toVertex==null) return;

//        根据起点和终点获得边,然后删除
        Edge<V,E> edge=new Edge<>(fromVertex,toVertex);
        if (fromVertex.outEdges.remove(edge)){ //表示删除成功
            toVertex.inEdges.remove(edge);
            edges.remove(edge);
        }
    }

完整代码

package graph;

import java.util.*;

public class ListGraph<V,E> implements Graph<V,E>{

    // 传入的V与顶点类Vertex的映射【要记录该点的出入是inEdges还是outEdges】
    private Map<V, Vertex<V, E>> vertices = new HashMap<>();
    // 边的Set集合
    private Set<Edge<V, E>> edges = new HashSet<>();

    public void print(){
        System.out.println("[顶点]-------------------");
        vertices.forEach((V v, Vertex<V, E> vertex) -> {
            System.out.println(v);
            System.out.println("out-----------");
            System.out.println(vertex.outEdges);
            System.out.println("int-----------");
            System.out.println(vertex.inEdges);
        });
        System.out.println("[边]-------------------");
        edges.forEach((Edge<V, E> edge) -> {
            System.out.println(edge);
        });
    }

    @Override
    public int edgesSize() {
        return edges.size();
    }

    @Override
    public int verticesSize() {
        return vertices.size();
    }

    @Override
    public void addVertex(V v) {
//        put(key,value)
        vertices.put(v,new Vertex<>(v));
    }

    /**
     * 添加无权值的边
     * @param from
     * @param to
     */
    @Override
    public void addEdge(V from, V to) {
        addEdge(from,to,null);
    }

    /**
     * 添加有权值的边
     * @param from
     * @param to
     * @param weight
     */
    @Override
    public void addEdge(V from, V to, E weight) {
        //根据传入的参数from找到出发点,如果不存在则创建
        Vertex<V,E> fromVertex=vertices.get(from);
        if (fromVertex==null){
            fromVertex=new Vertex<>(from);
            //将点和对应的点关系存入
            vertices.put(from,fromVertex);
        }
        //根据传入参数to找到终点,如果找不到则创建
        Vertex<V,E> toVertex=vertices.get(to);
        if (toVertex==null){
            toVertex=new Vertex<>(to);
            //将点和对应的点关系存入
            vertices.put(to,toVertex);
        }
        //根据出发点和终点,创建边
        Edge<V,E> edge=new Edge<>(fromVertex,toVertex);
        edge.weight=weight;//有权值加上,无权值则为null

        //不管原来是否存在,都先删除,在添加进去
        if (fromVertex.outEdges.contains(edge)){ //说明存在
            toVertex.inEdges.remove(edge);
            //在整个图中的边减少
            edges.remove(edge);
        }
        fromVertex.outEdges.add(edge);
        toVertex.inEdges.add(edge);
        //在整个图中的边增加
        edges.add(edge);

    }

    /**
     * 删除点
     * @param v
     */
    @Override
    public void removeVertex(V v) {
        //根据传入的值找到点并且删除,不存在则不做操作
        Vertex<V,E> vertex=vertices.remove(v);
        if (vertex==null) return;
//        迭代器遍历集合vertex.outEdges,删除所有从该点出去的边
//        iterator.hasNext():相当于i++
        for (Iterator<Edge<V,E>> iterator = vertex.inEdges.iterator();iterator.hasNext();){
            Edge<V,E> edge=iterator.next();//遍历从该点出去的边
            edge.to.inEdges.remove(edge);//获取终点进入的边,并从中删除遍历到的边
            //这个remove方法是将当前的这个边删除
            iterator.remove();//将当前遍历到当前遍历到的元素edge从集合vertex.outEdges中删除
            edges.remove(edge);
        }
        // 迭代器遍历集合vertex.inEdges, 删除所有进入该点的边
        for (Iterator<Edge<V, E>> iterator = vertex.inEdges.iterator(); iterator.hasNext();) {
            Edge<V, E> edge = iterator.next(); // 遍历到的进入该点的边
            edge.from.outEdges.remove(edge); // 获取起点出去的边,并从中删除遍历到的边
            iterator.remove(); // 将当前遍历到的元素edge从集合vertex.inEdges中删掉
            edges.remove(edge);
        }
    }

    /**
     * 删除边
     * @param from
     * @param to
     */
    @Override
    public void removeEdge(V from, V to) {
//        根据传入的from获得起点,不存在则不需要删除
        Vertex<V,E> fromVertex=vertices.get(from);
        if (fromVertex ==null){
            return;
        }
//        根据传入的to找到终点,不存在则不需要删除
        Vertex<V,E> toVertex=vertices.get(to);
        if (toVertex==null) return;

//        根据起点和终点获得边,然后删除
        Edge<V,E> edge=new Edge<>(fromVertex,toVertex);
        if (fromVertex.outEdges.remove(edge)){ //表示删除成功
            toVertex.inEdges.remove(edge);
            edges.remove(edge);
        }
    }

    /**
     * 顶点
     * @param <V>
     * @param <E>
     */
    private static class Vertex<V,E>{
        //顶点值
        V value;
        Set<Edge<V,E>> inEdges=new HashSet<>();
        Set<Edge<V,E>> outEdges=new HashSet<>();

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

        /**
         * 比较两个顶点是否相等
         * @param obj
         * @return
         */
        @Override
        public boolean equals(Object obj) {
            return Objects.equals(value,((Vertex<V,E>)obj).value);
        }

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

    /**
     * 边
     * @param <V>
     * @param <E>
     */
    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;
        }

        /**
         * 判断两条边是否相同
         * @param obj
         * @return
         */
        @Override
        public boolean equals(Object obj) {
            Edge<V,E> edge=(Edge<V, E>) obj;
            return Objects.equals(from,edge.from) && Objects.equals(to,edge.to);
        }

        /**
         * 如果equals的结果是true说明一样,则hashcode值也一样
         * @return
         */
        @Override
        public int hashCode() {
            return from.hashCode()+to.hashCode();
        }
    }
}

二、图的遍历

图的遍历

从图中某一顶点出发访问图中其余顶点,且每一个顶点仅被访问一次
图有2种常见的遍历方式(有向图、无向图都适用)

  • 广度优先搜索(Breadth First Search,BFS),又称为宽度优先搜索、横向优先搜索。
  • 深度优先搜索(Depth First Search,DFS)

发明“深度优先搜索”算法的2位科学家在1986年共同获得计算机领域的最高奖:图灵奖。

新的图接口

package graph;

import java.util.List;
import java.util.Set;

public interface Graph<V,E> {
//    边的数量
    int edgesSize();
//    顶点数量
    int verticesSize();

//    添加顶点
    void addVertex(V v);
//    添加边(无权值)
    void addEdge(V from,V to);
//    添加边(有权值)
    void addEdge(V from,V to,E weight);

//    删除顶点
    void removeVertex(V v);
//    删除边
    void removeEdge(V from,V to);

    void print();



    void bfs(V begin, vertexVisitor<V> visitor); // 广度优先搜索


    /**
     * 非递归版的深度优先搜索
     * @param begin
     * @param visitor
     */
    void dfs(V begin, vertexVisitor<V> visitor);


    List<V> topologicalSort(); // 拓扑排序

    interface vertexVisitor<V>{
        boolean visit(V v);
    }
}

三、广度优先搜索(BFS)

1.图解

之前所学的二叉树层序遍历就是一种广度优先搜索。

注:BFS结果不唯一

经历一层可以访问到的

 2.思路

将该点从队列取出来,然后再将其所子节点依次放入队列

从某个点开始,将它可以到达的点放入队列,如果已经访问过则跳过,然后从队列中取出点重复该过程。

第一层:假设从点A开始,它可以到达B、F,则将B、F入队。
此时队列中元素 [B、F]
第二层:队头B出队,B可以到达C、I、G,将C、I、G入队。
此时队列中元素 [F、C、I、G]
第三层:队头F出队,F可以到达G、E,但G已访问过,将E入队。
此时队列中元素 [C、I、G、E]
第四层:队头C出队,C可以到达I、D,但I已访问过,将D入队。
此时队列中元素 [I、G、E、D]
第五层:队头I出队,I可以到达D,但D已访问过,不执行操作。
此时队列中元素 [G、E、D]
第六层:队头G出队,G可以到达D、H,但D已访问过,将H入队。
此时队列中元素 [E、D、H]
第七层:队头E出队,E可以到达D、H、F,都访问过,不执行操作。
此时队列中元素 [D、H]
第八层:队头D出队,D可以到达C、H、E,都访问过,不执行操作。
此时队列中元素 [H]
第九层:队头H出队,H可以到达D、G、E,都访问过,不执行操作。
此时队列中元素 []
队列为空,广度优先搜索结束。

3.代码实现

    /**
     * 广度优先搜索:BFS
     * @param begin
     * @param visitor
     */
    @Override
    public void bfs(V begin, vertexVisitor<V> visitor) {
        if (visitor ==null) return;
        //根据传入的值begin找到顶点
        Vertex<V,E> beginVertex=vertices.get(begin);
        //该顶点不存在,不做操作
        if (beginVertex==null) return;

        //存放已经访问过的节点
        //因为如果不存放,则遍历到下一个顶点的时候,会将刚刚出队列的数值再一次放入
        Set<Vertex<V,E>> visitedVertices=new HashSet<>();
        //临时存放
        Queue<Vertex<V,E>> queue=new LinkedList<>();
        queue.offer(beginVertex); //元素入队
        //将已经加入过队列的元素标记一下
        visitedVertices.add(beginVertex);

        //思考参考二叉树层次遍历,队列存放每一层的顶点,用集合记录已经访问过的点
        while (!queue.isEmpty()){
            //从队列中取出第一个顶点
            Vertex<V,E> vertex=queue.poll();
            if (visitor.visit(vertex.value)) return;
            //遍历【从队列中取出的顶点】的出去的边,将【这些边的终点】入队,并且标记为已经访问过
            for (Edge<V,E> edge:vertex.outEdges){
                //如果集合中已经记录该顶点,说明已经访问过,跳过进行下一轮
                if (visitedVertices.contains(edge.to)) continue;
                queue.offer(edge.to);
                visitedVertices.add(edge.to);
            }
        }
    }

四、深度优先搜索(DFS)

之前所学的二叉树前序遍历就是一种深度优先搜索。

注:DFS结果不唯一。

1.前序遍历

 2.图解

 

3.递归实现

    /**
     * 递归实现--->深度优先遍历:DFS
     * @param begin
     * @param visitedVertices 已经访问过的节点
     */
    /**
     * 递归实现深度优先搜索DFS
     */
    public void dfs(V begin) {
        Vertex<V, E> beginVertex = vertices.get(begin); // 根据传入的值获取顶点
        if (beginVertex == null) return; // 顶点不存在则不执行操作
        dfs(beginVertex, new HashSet<>()); // 传入的集合,用来记录访问过的顶点
    }
    private void dfs(Vertex<V, E> vertex, Set<Vertex<V, E>> vistedVertices){
        System.out.println(vertex.value);
        vistedVertices.add(vertex);

        for(Edge<V, E> edge : vertex.outEdges){
            if(vistedVertices.contains(edge.to)) continue;
            //dfs会自己后退
            dfs(edge.to, vistedVertices);
        }
    }

 4.通过栈-----非递归实现

1)步骤

1.从outEdge中选择一条边

2.将选择边的from和to边,按顺序入栈【先入from再入to】

3.打印选择边的to

4.将to边添加到已经访问的范围中

5.break

2)图解

 3)代码实现

    /**
     * 非递归版(通过栈实现):深度优先搜索(DFS)
     * @param
     */

    @Override
    public void dfs(V begin, vertexVisitor<V> visitor) {
        if (visitor ==null) return;

        //查看该点是否存在
        Vertex<V,E> beginVertex=vertices.get(begin);
        if (begin==null) return;
        //创建一个set存放已经访问过的节点
        Set<Vertex<V,E>> visitedVertices=new HashSet<>();
        //创建一个栈临时存放
        Stack<Vertex<V,E>> stack=new Stack<>();

        //先访问起点
        stack.push(beginVertex);
        visitedVertices.add(beginVertex);
        //如果该节点已经访问过,则不再放入
        if (visitor.visit(begin)) return;

        while (!stack.isEmpty()){ //如果栈不为空
            Vertex<V,E> vertex=stack.pop();

            for (Edge<V,E> edge :vertex.outEdges){ //访问出发点的每一条边
                if (visitedVertices.contains(edge.to)) continue; //表示这条边已经访问过
                //找了一条适合且未遍历过的边
                stack.push(edge.from);//将出发点加入栈中
                stack.push(edge.to);//将出度点加入栈中
                //将加入栈中的节点加入已经访问过发范围中
                visitedVertices.add(edge.to);
                if (visitor.visit(edge.to.value)) return;
                break;
            }
        }
    }

五、AOV网(Activity On Vertex Network)

一项大的工程常被分为多个小的子工

子工程之间可能存在一定的先后顺序,即某些子工程必须在其他的一些子工程完成后才能开始。
在现代化管理中,人们常用有向图来描述和分析一项工程的计划和实施过程,子工程被称为活动(Activity)

以顶点表示活动、有向边表示活动之间的先后关系,这样的图简称为 AOV 网。【有很强的依赖关系】
标准的AOV网必须是一个有向无环图(Directed Acyclic Graph,简称 DAG)

六、拓扑排序(Topological Sort) 

1.简介

前驱活动:有向边起点的活动称为终点的前驱活动

  • 只有当一个活动的前驱全部都完成后,这个活动才能进行

后继活动:有向边终点的活动称为起点的后继活动

 2.思路

可以使用卡恩算法(Kahn于1962年提出)完成拓扑排序。

假设 L 是存放拓扑排序结果的列表:

  • ① 把所有入度为 0 的顶点放入 queue 中,然后把这些顶点从图中去掉,放入list中
  • ② 然后找到已经从queue中移除的入度为0的节点的下一个节点,将下一个节点的入度-1,如果-1后,入度为0,则直接加入queue
  • ③ 重复操作 ①,直到找不到入度为 0 的顶点
  • 如果此时 L 中的元素个数和顶点总数相同,说明拓扑排序完成
  • 如果此时 L 中的元素个数少于顶点总数,说明原图中存在环,无法进行拓扑排序

3.代码实现

    /**
     * 拓扑排序
     * @return
     */
    @Override
    public List<V> topologicalSort() {
        List<V> list = new ArrayList<>();
        Queue<Vertex<V, E>> queue = new LinkedList<>();
        Map<Vertex<V, E>, Integer> ins = new HashMap<>();

        // 初始化(将度为0的节点放入队列)
        vertices.forEach((V v, Vertex<V, E> vertex) -> {
            int indegree = vertex.inEdges.size(); // 入度
            if(indegree == 0) { // 入度为0,放入队列
                queue.offer(vertex);
            } else { // 入度不为0,用map记录它的入度
                ins.put(vertex, indegree);
            }
        });

        while(!queue.isEmpty()){ // 从队列中取节点
            //取出队列中的第一个元素
            Vertex<V, E> vertex = queue.poll();
            list.add(vertex.value); // 放入返回结果中

            //遍历取出的节点的出度边
            for (Edge<V, E> edge : vertex.outEdges){
                // 队列中取出节点所通向节点的入度
                int toIndegree = ins.get(edge.to) - 1;
                if(toIndegree == 0) { // 入度为0,放入队列
                    queue.offer(edge.to);
                } else { // 入度不为0,用map记录它的入度
                    ins.put(edge.to, toIndegree);
                }
            }
        }

        return list;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/417217.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微服务架构-服务网关(Gateway)-权限认证(分布式session替代方案)

权限认证-分布式session替代方案 前面我们了解了Gateway组件的过滤器&#xff0c;这一节我们就探讨一下Gateway在分布式环境中的一个具体用例-用户鉴权。 1、传统单应用的用户鉴权 从我们开始学JavaEE的时候&#xff0c;就被洗脑式灌输了一种权限验证的标准做法&#xff0c;…

Adobe全新AI工具引关注,Adobe firefly助力创作更高效、更有创意

原标题&#xff1a;Adobe全新AI工具引关注&#xff0c;Adobe firefly&#xff08;萤火虫&#xff09;助力创作更高效、更有创意。 以ChatGPT为首的生成式AI、AIGC等工具的战局正如火如荼的进行中..... 除了微软、百度的聊天机器人和一些初创公司的AI画图工具令人惊艳&#xff…

Greenplum数据库执行器——PartitionSelector执行节点

为了能够对分区表有优异的处理能力&#xff0c;对于查询优化系统来说一个最基本的能力就是做分区裁剪partition pruning&#xff0c;将query中并不涉及的分区提前排除掉。如下执行计划所示&#xff0c;由于单表谓词在parititon key上&#xff0c;在优化期间即可确定哪些可以分区…

003:Mapbox GL设定不同的投影方式

第003个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中设定不同的投影方式 。默认情况下为Mercator投影,或者设置为null或者undefined时候,显示为Mercator投影。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源…

【分享】维格表集成易聊实现线索自动化,减少流失率

公司•介绍 北京某职业教育公司专注行业发展、国际就业、留学、移民咨询。秉承专业性至上的原则&#xff0c;与行业内专家、高等学府以及产业集团合作&#xff0c;并邀请各领域专家组建了强大的专委会团队&#xff0c;为公司的业务开展提供专业性支持。 客户•遇到的问题 作为…

【Java面试八股文宝典之MySQL篇】备战2023 查缺补漏 你越早准备 越早成功!!!——Day23

大家好&#xff0c;我是陶然同学&#xff0c;软件工程大三即将实习。认识我的朋友们知道&#xff0c;我是科班出身&#xff0c;学的还行&#xff0c;但是对面试掌握不够&#xff0c;所以我将用这100多天更新Java面试题&#x1f643;&#x1f643;。 不敢苟同&#xff0c;相信大…

用Spring Doc代替Swagger

1 OpenApi OpenApi 是一个业界的 API 文档标准&#xff0c;是一个规范&#xff0c;这个规范目前有两大实现&#xff0c;分别是&#xff1a; SpringFoxSpringDoc 其中 SpringFox 其实也就是我们之前所说的 Swagger&#xff0c;SpringDoc 则是我们今天要说的内容。 OpenApi 就…

苹果智能戒指专利曝光,Find My技术加持不易丢

根据美国商标和专利局&#xff08;USPTO&#xff09;公示的清单&#xff0c;苹果近日获得了一项“智能戒指”相关的设计专利&#xff0c;编号为“US 11625098 B2”。 这款智能戒指专利主要服务于增强现实&#xff08;AR&#xff09;或者虚拟现实&#xff08;VR&#xff09;场…

leetcodeTmp

39. 组合总和 39. 组合总和 DFS排列&#xff1a;每个元素可选0次&#xff0c;1次以及多次 public List<List<Integer>> combinationSum(int[] candidates, int target) {//Arrays.sort(candidates);//注释了也能通过this.candidates candidates;ans.clear();co…

Omniverse Replicator 入门

OmniverseReplicator入门 Omniverse Replicator 作为 Omniverse Kit 扩展创建&#xff0c;并通过 Omniverse Code 方便地分发。 要使用复制器&#xff0c;您需要下载可在此处找到的 Omniverse 启动器。 有关 Omniverse 启动器的更多详细信息&#xff0c;请查看此视频。 使用…

kafaka学习

kafaka 消息队列&#xff1a;通常用来解决一个进程内&#xff0c;多线程环境下&#xff0c;资源竞争的问题&#xff1b;但是消息队列的锁的粒度太大了&#xff0c;需要进行拆分 消息队列中间组件 一个进程中&#xff0c;同时存在生产者、消费者、消息队列&#xff0c;在分布…

网络文件传输防止篡改-校验工具(md5sum)的使用

说明 MD5报文摘要算法&#xff08;Message-Digest Algorithm 5&#xff09;常常被用来验证网络文件传输的完整性&#xff0c;防止文件被人篡改。此算法对任意长度的信息逐位进行计算&#xff0c;产生一个二进制长度为128位&#xff08;十六进制长度就是32位&#xff09;的“指…

wordpres漏洞扫描器——wpscan

WordPress 使用PHP语言开发的博客平台 WordPress是使用PHP语言开发的博客平台&#xff0c;用户可以在支持PHP和MySQL数据库的服务器上架设属于自己的网站。也可以把 WordPress当作一个内容管理系统&#xff08;CMS&#xff09;来使用。 WordPress是一款个人博客系统&#xff0c…

手把手教你在linux中部署stable-diffusion-webui

stable-diffusion-webui是什么就不用多说了&#xff0c;以下是安装步骤&#xff0c;我以linux系统为例介绍&#xff0c;windows系统大同小异&#xff0c;安装期间没有用到梯子&#xff0c;安装目录/opt/stable-diffusion-webui/。 1.安装Anaconda stable-diffusion-webui要求p…

2023年小红书用户种草转化新路径

随着消费者对商品选择性提高&#xff0c;品牌转化链路随之被拉长&#xff0c;在投放操盘上竞争也愈发激烈&#xff0c;本期和大家聊聊如何在关键节点上引领用户决策&#xff0c;完成用户种草转化。种草链路拉长品牌发力点在何处&#xff1f; 基于平台用户的洞察分析&#xff0c…

ESXi安装CentOS

ESXi安装 参考&#xff1a;https://blog.csdn.net/tongxin_tongmeng/article/details/129466704 CentOS安装 镜像&#xff1a;http://mirrors.aliyun.com/centos/7/isos/x86_64-->CentOS-7-x86_64-DVD-2009.iso CentOS配置 FinalShell连接 ESXi简介 1.ESXi是由VMware公司…

leedcode刷题(6)

各位朋友们大家好&#xff0c;今天是我的leedcode刷题系列的第六篇。这篇文章将与队列方面的知识相关&#xff0c;因为这些知识用C语言实现较为复杂&#xff0c;所以我们就只使用Java来实现。 文章目录设计循环队列题目要求用例输入提示做题思路代码实现用栈实现队列题目要求用…

【回溯法】-----求一个集合的子集问题

leetcode78 subsetsleetcode 78 问题原文ExampleConstraints:解决思路回溯法代码实现leetcode 78 问题原文 Given an integer array nums of unique elements, return all possible subsets (the power set). The solution set must not contain duplicate subsets. Return t…

银行数仓分层架构

一、为什么要对数仓分层 实现好分层架构&#xff0c;有以下好处&#xff1a; 1清晰数据结构&#xff1a; 每一个数据分层都有对应的作用域&#xff0c;在使用数据的时候能更方便的定位和理解。 2数据血缘追踪&#xff1a; 提供给业务人员或下游系统的数据服务时都是目标数据&…

WebTest搭建

0.前言 此框架为真实项目实战&#xff0c;所以有些数据不便展示&#xff0c;只展示架构和思想 工具&#xff1a;pythonseleniumddtunittest 1.架构说明 2.代码封装 Commom层 base_page.py #__author__19044168 #date2021/8/26 import logging import datetime from sele…