算法.图论-建图/拓扑排序及其拓展

news2025/2/23 12:02:41

文章目录

    • 建图的三种方式
      • 邻接矩阵
      • 邻接表
      • 链式前向星
    • 拓扑排序
      • 拓扑排序基础原理介绍
      • 拓扑排序步骤解析
      • 拓扑排序模板leetcode-课程表
    • 拓扑排序拓展
      • 食物链计数
      • 喧闹与富有
      • 并行课程

建图的三种方式

我们建图的三种方式分别是邻接矩阵, 邻接矩阵, 链式前向星

邻接矩阵

假设我们的点的个数为N个, 我们就把他们的下标依次标为1, 2 ,…, 然后在一个矩阵表上进行边的添加, 比如我要在点2和点4之间添加一个权值为5的边, 那么我就在矩阵matrix[2][4] = 5即可, 唯一注意的就是如果是无向图, 就需要把matrix[4][2]位置也设置为5, 所以这个方法的弊端是十分明显的, 就是消耗的空间过大, 所以我们在大厂的笔试或者是比赛, 不会用这种方式进行建图, 下面是邻接矩阵法的代码实现

public class day23 {
    public static void main(String[] args) {
        int[][] edges = {{1,2,5},{5,3,1},{1,4,4},{2,5,2}};
        //测试一下使用邻接矩阵法建图
        GraphUseMatrix graphUseMatrix = new GraphUseMatrix();
        graphUseMatrix.build(5);
        graphUseMatrix.directGraph(edges);
        //graphUseMatrix.unDirectGraph(edges);
        graphUseMatrix.showGraph();
    }
}

/**
 * 邻接矩阵建图的方法
 * 下面我们介绍的所用的图都是带权值的图, 不带权的更简单就不说了
 */
class GraphUseMatrix{
    //设置一个最大的点数(根据题意)
    private static final int MAXN = 11;
    //设置一个最大的边数(根据题意, 无向图 * 2)
    private static final int MAXM = 21;

    //构建一个当前的点数curn
    private static int curN = 0;
    //设置一个邻接的矩阵(大小就是(点数 + 1) * (点数 + 1), 这里我们保证是够用的)
    private static final int[][] matrix = new int[MAXN][MAXN];

    //初始化矩阵的方法(传入的点的数量)
    public void build(int n){
        curN = n;
        //清空矩阵的脏数据
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                //把邻接矩阵中的数据刷为最大值(因为权值可能为0)
                matrix[i][j] = Integer.MAX_VALUE;
            }
        }
    }

    //添加边的方法
    private void addEdge(int from, int to, int weight){
        matrix[from][to] = weight;
    }

    //在有向带权图中添加边
    public void directGraph(int[][] edges){
        for(int[] edge : edges){
            addEdge(edge[0], edge[1], edge[2]);
        }
    }

    //在无向带权图中添加边
    public void unDirectGraph(int[][] edges){
        for(int[] edge : edges){
            addEdge(edge[0], edge[1], edge[2]);
            addEdge(edge[1], edge[0], edge[2]);
        }
    }

    //展示图的方式
    public void showGraph(){
        System.out.println("邻接矩阵法建图展示");
        for(int i = 1; i <= curN; i++){
            for(int j = 1; j <= curN; j++){
                if(matrix[i][j] == Integer.MAX_VALUE){
                    System.out.print("∞  ");
                }else{
                    System.out.print(matrix[i][j] + "  ");
                }
            }
            System.out.println();
        }
    }
}

上述代码的测试结果见下
在这里插入图片描述
在这里插入图片描述

测试结果也是很明显的, 证明我们之前的代码是没有问题的, 无向图总体是按照正对角线呈对称分布

邻接表

邻接表是一种动态的建图的方式, 关于大厂的笔试面试题, 邻接表以及完全够用了, 如果涉及到比赛内容的话, 我们会使用链式前向星建图法, 等会我们会介绍到这种算法, 说回来邻接表, 其实就是一个
ArrayList<ArrayList<int(无权)/int>的结构, 也就是顺序表套顺序表的结构, 假如我们要建立一个从点2到点4的边,如果不带权值, 我们就让外层顺序表2下标对应的顺序表添加一个元素4, 如果同时带有一个权值的话, 我们就添加一个数组(假设权值是8)我们就让2下标对应的顺序表添加一个[4,8]数组, 下面是邻接表的代码实现

public class day23 {
    public static void main(String[] args) {
        int[][] edges = {{1, 2, 5}, {5, 3, 1}, {1, 4, 4}, {2, 5, 2}};
//        //测试一下使用邻接矩阵法建图
//        GraphUseMatrix graphUseMatrix = new GraphUseMatrix();
//        graphUseMatrix.build(5);
//        graphUseMatrix.unDirectGraph(edges);
//        graphUseMatrix.showGraph();

        //测试一下邻接表法建图
        GraphUseList graphUseList = new GraphUseList();
        graphUseList.build(5);
        graphUseList.directGraph(edges);
        //graphUseList.unDirectGraph(edges);
        graphUseList.showGraph();
    }
}

/**
 * 邻接表建图的方法(是一种动态的结构)
 * 和邻接矩阵一样, 我们在这里介绍的都是带权的图
 */
class GraphUseList {

    //构建一个当前的点数
    private static int curN = 0;

    //邻接表的主体
    private static ArrayList<ArrayList<int[]>> list = new ArrayList<>();

    //初始化邻接表(传入一个点的数量)
    public void build(int n) {
        //设置当前的点数
        curN = n;
        //上来直接清空邻接表
        list.clear();
        //开始构建顺序表(新添加n+1个列表)
        for (int i = 0; i <= n; i++) {
            list.add(new ArrayList<>());
        }
    }

    //添加边的方法
    private void addEdge(int from, int to, int weight) {
        list.get(from).add(new int[]{to, weight});
    }

    //构建一个有向的图
    public void directGraph(int[][] edges) {
        for (int[] edge : edges) {
            addEdge(edge[0], edge[1], edge[2]);
        }
    }

    //构建一个无向的图
    public void unDirectGraph(int[][] edges) {
        for (int[] edge : edges) {
            addEdge(edge[0], edge[1], edge[2]);
            addEdge(edge[1], edge[0], edge[2]);
        }
    }

    //展示邻接表图的方式
    public void showGraph() {
        System.out.println("邻接表法建图展示");
        for (int i = 1; i <= curN; i++) {
            System.out.print("点" + i + "(邻点/权值) : ");
            for (int[] elem : list.get(i)) {
                System.out.print("[" + elem[0] + "," + elem[1] + "]");
            }
            System.out.println();
        }
    }
}

执行结果如下图所示
在这里插入图片描述
在这里插入图片描述

链式前向星

前两种建图的方式都有着明显的缺点, 第一个虽然是静态空间但是空间的大小过大, 第二个虽然使用的空间不是很大但是是一种动态的结果, 就会在时间上大打折扣, 所以我们就想要一种既可以是静态空间又可以做到省空间省时间的结构, 我们就需要下面的链式前向星建图法, 这个方法有点类似与前缀树的静态空间建树法(之前有)和链表头插法的结合, 下面我们分析一下建图的过程

准备过程 :
我们准备三个数组, 把每一个加入的边都设置一个编号(从1开始逐渐增加)

数组数组解释
head数组下标表示点的编号, head[i]表示这个点的’头边’的编号
next数组下标表示边的编号, next[i]表示这个边的下一条边的编号
to数组下标表示边的编号, to[i]表示这条边到达的点的编号
weight数组下标表示边的编号, weight[i]表示这条边的权值

具体的例子
假设此时我们准备了五个点, 然后执行4次加边的操作
head数组长度准备为 6 (5 + 1) , next, to, weight 数组的长度都准备 5 (4 + 1)
下面执行加边的过程

[3,1,2], 这条边编号为1, 出发点是3, 终点是1, 权值是2, 在head中插入头边head[3] = 1(这里就是用的头插法, 这里是首条边), next[1] = 0 (头边所以是0) , to[1] = 1, weight[1] = 2

代码实现如下

public class day23 {
    public static void main(String[] args) {
        int[][] edges = {{1, 2, 5}, {5, 3, 1}, {1, 4, 4}, {2, 5, 2}};
//        //测试一下使用邻接矩阵法建图
//        GraphUseMatrix graphUseMatrix = new GraphUseMatrix();
//        graphUseMatrix.build(5);
//        graphUseMatrix.unDirectGraph(edges);
//        graphUseMatrix.showGraph();

//        //测试一下邻接表法建图
//        GraphUseList graphUseList = new GraphUseList();
//        graphUseList.build(5);
//        graphUseList.unDirectGraph(edges);
//        graphUseList.showGraph();

        //测试一下链式前向星建图
        GraphUseLinkedStar graphUseLinkedStar = new GraphUseLinkedStar();
        graphUseLinkedStar.build(5);
        graphUseLinkedStar.unDirectGraph(edges);
        graphUseLinkedStar.showGraph();
    }
}


/**
 * 链式前向星建图法(静态的建图法)
 */
class GraphUseLinkedStar {
    //定义点最大值
    private static final int MAXN = 11;
    //定义边最大值
    private static final int MAXM = 22;
    //构建head数组(以点为准)
    private static final int[] head = new int[MAXN];
    //构建next数组(以边为准)
    private static final int[] next = new int[MAXM];
    //准备to数组(以边为主)
    private static final int[] to = new int[MAXM];
    //准备weight数组(以边为主)
    private static final int[] weights = new int[MAXM];
    //定义一下当前点的个数
    private static int curN = 0;
    //定义一个计数器用于给边编号
    private static int cnt = 0;

    //传入一个点数n用来初始化图结构
    public void build(int n){
        //更新计数器
        cnt = 1;
        //初始化当前节点个数
        curN = n;
        //清除head即可(这里不用重置to, weights, next)
        Arrays.fill(head, 1, n + 1, 0);
    }

    //添加边的方法(其实就是链表的头插法)
    private void addEdge(int from, int ton, int weight){
        next[cnt] = head[from];
        to[cnt] = ton;
        weights[cnt] = weight;
        head[from] = cnt++;
    }

    //构建一个有向带权图
    public void directGraph(int[][] edges){
        for(int[] edge : edges){
            addEdge(edge[0], edge[1], edge[2]);
        }
    }

    //构建一个无向带权图
    public void unDirectGraph(int[][] edges){
        for(int[] edge : edges){
            addEdge(edge[0],edge[1],edge[2]);
            addEdge(edge[1],edge[0],edge[2]);
        }
    }

    //展示图的方法(类似于链表的遍历)
    public void showGraph(){
        System.out.println("链式前向星建图展示");
        for(int i = 1; i <= curN; i++){
            System.out.print("点" + i + "(邻点/权值) : ");
            for(int ei = head[i]; ei != 0; ei = next[ei]){
                System.out.print("[" + to[ei] + "," + weights[ei] + "]");
            }
            System.out.println();
        }
    }
}

执行结果如下
在这里插入图片描述

在这里插入图片描述

拓扑排序

拓扑排序基础原理介绍

拓扑排序的存在条件是在一个有向且无环图中的排序, 拓扑排序在某种程度上反应的是一件事的执行的先后顺序, 请看下图

在这里插入图片描述
这张图中, 黑色的字符表示节点的名称, 蓝色的数字指的是该位置的入度是多少, 上面我们提到过, 拓扑排序的过程可以视为完成某一件事的先后顺序, 假设我们最终想要完成的任务是f, 那我们下面的字符的序列也就是完成最终事件的顺序
拓扑排序不是唯一的, 比如下面的图
在这里插入图片描述
在这张图中, 先完成a还是先完成b都是可以的, 所以产生的拓扑排序的情况就有两种, 这就说明拓扑排序的结果可能有多种, 只要满足每一个节点的前面所需要的节点都完成了就可以
拓扑排序的应用举例 :
比如在计算机编译程序的过程中, 编译一个程序需要另外的程序结果才能编译完成, 所以很自然的就会出现程序编译的先后顺序问题, 见下图, 左侧的表格是编译一个程序所需要的已经完成的编译结果, 右面是完成a的编译过程的图, 右下角的两串字符串都是完成的顺序, 这同样说明拓扑排序不是唯一的
在这里插入图片描述
拓扑排序要求一个图有向, 这个很好理解, 做事情的步骤肯定是有先后的顺序的, 而且要无环, 这个我们就拿上面的编译过程理解, 假如a的编译过程依赖b, b的编译过程依赖a, 那这不就是矛盾了吗, 两者互相依赖谁也完成不了

拓扑排序步骤解析

实现拓扑排序用的是零入度删除法, 需要用到队列(特殊状况下用小根堆), 核心就是删除0入度的节点和因为该节点所造成的入度影响
这就解释了上面的图为什么我们要进行入度的标记, 首先图解一下0入度删除法的步骤
在这里插入图片描述
最终组合出来的删除节点的步骤就是最终的拓扑排序的答案

拓扑排序模板leetcode-课程表

leetcode210-课程表题目链接
在这里插入图片描述

class Solution {
    //我们采用邻接表建图就已经够用了
    private static ArrayList<ArrayList<Integer>> list = new ArrayList<>();
    //课的最大数目
    private static final int MAXN = 2001;
    //同时定义一个入读表
    private static final int[] indegree = new int[MAXN];
    //当前的数据个数
    private static int curN = 0;

    public int[] findOrder(int numCourses, int[][] prerequisites) {
        build(numCourses);
        directGraph(prerequisites);
        //定义一个计数器看一看队列弹出了多少次(建议使用数组形式队列)
        int cntNum = 0;
        Queue<Integer> queue = new ArrayDeque<>();
        //首先遍历入度数组加入入度为0的节点
        for (int i = 0; i < numCourses; i++) {
            if (indegree[i] == 0) {
                queue.offer(i);
            }
        }

        //从队列中逐渐弹出元素进行判断
        int[] res = new int[numCourses];
        while (!queue.isEmpty()) {
            int index = queue.poll();
            //在返回数组中添加上该元素
            res[cntNum++] = index;
            //遍历其所属的列表删除这个元素造成的入度
            for(int elem : list.get(index)){
                if(--indegree[elem] == 0){
                    queue.offer(elem);
                }
            }
        }
        return cntNum == res.length ? res : new int[0];
    }

    //初始化图结构
    private static void build(int n) {
        //更新当前的数据个数
        curN = n;
        //更新顺序表
        list.clear();
        for (int i = 0; i < n; i++) {
            list.add(new ArrayList<>());
        }
        //更新入度表
        Arrays.fill(indegree, 0, n, 0);
    }

    //建图的主函数(本质是有向无权图), 建图的过程中同时完成入度的统计
    private static void directGraph(int[][] edges) {
        for (int[] edge : edges) {
            addEdge(edge[1], edge[0]);
            indegree[edge[0]]++;
        }
    }

    //插入边的函数
    private static void addEdge(int from, int to) {
        list.get(from).add(to);
    }
}

文章目录

    • 建图的三种方式
      • 邻接矩阵
      • 邻接表
      • 链式前向星
    • 拓扑排序
      • 拓扑排序基础原理介绍
      • 拓扑排序步骤解析
      • 拓扑排序模板leetcode-课程表
    • 拓扑排序拓展
      • 食物链计数
      • 喧闹与富有
      • 并行课程

拓扑排序拓展

关于拓扑排序的最基础的用法我们在上一节就已经探讨过了, 这节课我们讨论的是拓扑排序的最重要的技巧, 就是根据拓扑排序逐段的向下推送消息, 其实有点类似于树形dp

食物链计数

在这里插入图片描述
上面我们仅仅介绍了一部分流程而已, 但是整体上的逻辑就是这样的, 建图之后跑拓扑排序, 然后计数即可
洛谷-食物链测试链接
代码实现如下, 使用的是链式前向星的建图方式, 然后注意一下比赛平台的IO方式即可

import java.util.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.io.OutputStreamWriter;
import java.io.IOException;
//计算食物链的条数, 用的是一种向下推送的技巧(建图使用链式前向星建图法)
public class Main{

    private static final int MAXN = 5001;

    private static final int MAXM = 500001;

    private static final int[] head = new int[MAXN];

    private static final int[] next = new int[MAXM];

    private static final int[] to = new int[MAXM];

    private static final int[] indegree = new int[MAXN];

    private static final int[] ans = new int[MAXN];

    private static int resCnt = 0;

    private static int cnt = 0;

    private static final int MOD = 80112002;

    //拓扑排序时需要的队列我们就使用动态的结构模拟看看过不过
    private static final Queue<Integer> queue = new ArrayDeque<>();

    //初始化图的函数
    private static void build(int n){
        cnt = 1;
        resCnt = 0;
        Arrays.fill(head, 0, n + 1, 0);
        Arrays.fill(indegree, 0, n + 1, 0);
        Arrays.fill(ans, 0, n + 1, 0);
        queue.clear();
    }

    //添加边的函数(链式前向星的加边方式, 同时修改入度)
    private static void addEdge(int fro, int t){
        next[cnt] = head[fro];
        to[cnt] = t;
        head[fro] = cnt++;
        indegree[t]++;
    }

    //拓扑排序的过程
    private static void topoSort(int n){
        //首先添加0入度的点入队列(同时在ans中修改等待推送)
        for(int i = 1; i <= n; i++){
            if(indegree[i] == 0){
                queue.offer(i);
                ans[i] = 1;
            }
        }

        //开始进行排序的主逻辑
        while(!queue.isEmpty()){
            int po = queue.poll();
            if(head[po] == 0){
                //此时说明该点是末尾的节点, 直接计数
                resCnt = (ans[po] + resCnt) % MOD;
            }else{
                //此时继续链式前向星的遍历过程
                for(int ei = head[po]; ei > 0; ei = next[ei]){
                    ans[to[ei]] = (ans[to[ei]] + ans[po]) % MOD;
                    if(--indegree[to[ei]] == 0){
                        queue.offer(to[ei]);
                    }
                }
            }
        }
    }
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer in = new StreamTokenizer(br);
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));

        //下面的while循环其实就是建图的过程
        while(in.nextToken() != StreamTokenizer.TT_EOF){
            int n = (int)in.nval;
            build(n);
            in.nextToken();
            int opSize = (int)in.nval;
            for(int i = 0; i < opSize; i++){
                in.nextToken();
                int fro = (int)in.nval;
                in.nextToken();
                int t = (int)in.nval;
                addEdge(fro, t);
            }
            //下面就去跑拓扑排序
            topoSort(n);
            out.println(resCnt);
        }
        
        out.flush();
        out.close();
        br.close();
    }
}

喧闹与富有

在这里插入图片描述
leetcode851.喧闹与富有题目链接
总体上说这个题没什么分析的难度, 就是简单的建出来图之后然后跑拓扑排序就可以了, 传递的信息就是最安静的人的下标, 注意初始化ans数组的时候就把ans数组中每个位置的值初始化为本身的下标(因为自己的安静值肯定是一个限制值)
代码实现如下

//使用链式前向星的建图方式
class LoudAndRich {
    
    private static final int MAXN = 501;

    private static final int MAXM = MAXN * (MAXN - 1) / 2;

    private static final int[] head = new int[MAXN];

    private static final int[] next = new int[MAXM];

    private static final int[] to = new int[MAXM];

    private static final int[] indegree = new int[MAXN];

    private static int cnt = 0;

    private static int curN = 0;

    private static final Queue<Integer> queue = new ArrayDeque<>();

    //初始化图的函数
    private static void build(int n){
        cnt = 1;
        curN = n;
        Arrays.fill(head, 0, n + 1, 0);
        Arrays.fill(indegree, 0, n + 1, 0);
        queue.clear();
    }

    //添加边的函数(同时统计入度)
    private static void addEdge(int fro, int t){
        next[cnt] = head[fro];
        to[cnt] = t;
        head[fro] = cnt++;
        indegree[t]++;
    }

    public int[] loudAndRich(int[][] richer, int[] quiet) {
        //建图
        int len = quiet.length;
        build(len);
        for(int[] edge : richer){
            addEdge(edge[0], edge[1]);
        }

        //初始化一个答案数组
        int[] ans = new int[len];
        for(int i = 0; i < len; i++){
            ans[i] = i;
        }
        //跑拓扑排序
        topoSort(ans, quiet);
        return ans;
    }

    private static void topoSort(int[] ans, int[] quiet){
        //首先找到0入度的点加入队列
        for(int i = 0; i < curN; i++){
            if(indegree[i] == 0){
                queue.offer(i);
            }
        }

        //遍历图进行ans数组的修改
        while(!queue.isEmpty()){
            int curPointer = queue.poll();
            for(int ei = head[curPointer]; ei > 0; ei = next[ei]){
                if(--indegree[to[ei]] == 0){
                    queue.offer(to[ei]);
                }
                ans[to[ei]] = quiet[ans[curPointer]] < quiet[ans[to[ei]]] ? ans[curPointer] : ans[to[ei]];
            }
        }
    }
}

并行课程

其实就是一个简单的拓扑排序, 建图的时候我们使用了一个新的辅助空间
代码实现如下


class Cource {

    private static final int MAXN = 50001;

    private static final int MAXM = 50001;

    private static final int[] head = new int[MAXN];

    private static final int[] next = new int[MAXM];

    private static final int[] to = new int[MAXM];

    private static final int[] indegree = new int[MAXN];

    private static int curN = 0;

    private static int cnt = 0;

    private static int res = Integer.MIN_VALUE;

    private static final Queue<Integer> queue = new ArrayDeque<>();

    //专属于本题的辅助数组(用于结算新加的time)
    private static final int[] need = new int[MAXN];

    private static final int[] ans = new int[MAXN];

    //初始化图的函数
    private static void build(int n, int[] time){
        cnt = 1;
        curN = n;
        res = Integer.MIN_VALUE;
        Arrays.fill(head, 0, n + 1, 0);
        Arrays.fill(need, 0, n + 1, 0);
        Arrays.fill(indegree, 0, n + 1, 0);
        queue.clear();
        //初始化ans数组
        for(int i = 1; i <= time.length; i++){
            res = Math.max(res, time[i - 1]);
            ans[i] = time[i - 1];
        }
    }

    //添加边的函数(顺带统计入度)
    private static void addEdge(int fro, int t){
        next[cnt] = head[fro];
        to[cnt] = t;
        head[fro] = cnt++;
        indegree[t]++;
    }

    public int minimumTime(int n, int[][] relations, int[] time) {
        //初始化并建图
        build(n, time);
        for(int[] edge : relations){
            addEdge(edge[0], edge[1]);
        }

        //下面就是拓扑排序的主流程
        topoSort();
        return res;
    }

    private static void topoSort(){
        //首先添加入度为0的点入队列
        for(int i = 1; i <= curN; i++){
            if(indegree[i] == 0){
                queue.offer(i);
            }
        }

        //开始进行拓扑排序的主流程
        while(!queue.isEmpty()){
            int courceNum = queue.poll();
            if(indegree[courceNum] == 0){
                //说明这个时候可以结算了
                ans[courceNum] += need[courceNum];
                res = Math.max(res, ans[courceNum]);
            }else{
                for(int ei = head[courceNum]; ei > 0; ei = next[ei]){
                    need[to[ei]] = Math.max(need[to[ei]], ans[courceNum]);
                    if(--indegree[to[ei]] == 0){
                        queue.offer(to[ei]);
                    }
                }
            }
        }
    }
}

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

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

相关文章

自定义复杂AntV/G6案例

一、效果图 二、源码 /** * * Author: me * CreatDate: 2024-08-22 * * Description: 复杂G6案例 * */ <template><div class"moreG6-wapper"><div id"graphContainer" ref"graphRef" class"graph-content"></d…

计算机毕业设计污染物文献共享数据库管理系统网站开发与实现

计算机毕业设计&#xff1a;污染物文献共享数据库管理系统网站开发与实现 1. 项目背景 随着环境问题日益严峻&#xff0c;对污染物的研究变得尤为重要。然而&#xff0c;在学术界和工业界之间存在着信息孤岛现象&#xff0c;即大量的研究成果被分散在不同的数据…

[Redis][String][上]详细讲解

目录 0.前言1.常见命令1.SET2.GET3.MSET && MGET4.SETNX && SETXX 2.计数命令1.INCR2.INCRBY3.DECR4.DECYBY5.INCRBYFLOAT6.注意 0.前言 字符串类型是Redis最基础的数据类型&#xff0c;关于字符串需要特别注意&#xff1a; Redis中所有的键的类型都是字符串类…

【Elasticsearch系列十四】Elasticsearch

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

大数据概念与价值

文章目录 引言大数据的概念高德纳咨询公司的定义麦肯锡全球研究所的定义什么是大数据&#xff1f; 大数据的特征Volume&#xff08;体积&#xff09;Variety&#xff08;种类&#xff09;Velocity&#xff08;速度&#xff09;Value&#xff08;价值&#xff09;Veracity&#…

Apache Hudi现代数据湖核心技术概论

1. 什么是 Apache Hudi 1.1 简介 Apache Hudi (Hadoop Upserts Deletes and Incrementals) 是一个开源的数据湖框架&#xff0c;旨在提供高效的数据管理和数据更新功能。它允许在大数据平台上执行诸如数据插入、更新和删除操作&#xff0c;同时支持增量式数据处理。Hudi 最初…

React18入门教程

React介绍 React由Meta公司开发&#xff0c;是一个用于 构建Web和原生交互界面的库 React的优势 相较于传统基于DOM开发的优势 组件化的开发方式 不错的性能 相较于其它前端框架的优势 丰富的生态 跨平台支持 React的市场情况 全球最流行&#xff0c;大厂必备 开发环境…

EmguCV学习笔记 C# 12.2 WeChatQRCode

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

Vue.js的前端框架有哪些?

Vue.js 是一款流行的前端 JavaScript 框架&#xff0c;用于构建单页面应用&#xff08;SPA&#xff09;。除了 Vue.js 本身&#xff0c;还有许多基于 Vue.js 的前端框架和 UI 库&#xff0c;它们提供了更多的功能和组件&#xff0c;以便开发者能够快速构建应用程序。以下是一些…

【图像压缩与重构】基于BP神经网络

课题名称&#xff1a;基于BP神经网络的图像压缩与重构&#xff08;带GUI) 相关资料&#xff1a; 1. 代码注释 2.BP神经网络原理文档资料 3.图像压缩原理文档资料 程序实例截图&#xff1a;

eclipse git 不小心点了igore,文件如何加到git中去。

1、创建了文件&#xff0c;或者利用三方工具&#xff0c;或者用mybatis plus生成了文件以后&#xff0c;我们需要右键文件&#xff0c;然后加入到git中。 右键有问号的java文件 -- Team -- Add to Index &#xff0c;然后变成个号就可以了。 2、不小心&#xff0c;点了一下Ign…

Mac 上哪个剪切板增强工具比较好用? 好用剪切板工具推荐

在日常文字编辑中&#xff0c;我们经常需要重复使用复制的内容。然而&#xff0c;新内容一旦复制&#xff0c;旧内容就会被覆盖。因此&#xff0c;选择一款易用高效的剪贴板工具成为了许多人的需求。本文整理了一些适用于 macOS 系统的优秀剪贴板增强工具&#xff0c;欢迎大家下…

华为OD机试 - 构成指定长度字符串的个数(Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

想高效开发,也许可以试试文件系统。。。

众所周知&#xff0c;4G-Cat.1模组的文件系统与数据传输速度、存储效率&#xff0c;以及数据安全性等有非常重要的关系&#xff0c;在应用开发中也非常重要。 今天我们来尝试Air201的实用示例——文件系统的使用 Air201文件系统的使用 合宙Air201资产定位模组——是一个集成超…

密集行人数据集 CrowdHumanvoc和yolo两种格式,yolo可以直接使用train val test已经划分好有yolov8训练200轮模型

密集行人数据集 CrowdHuman voc和yolo两种格式&#xff0c;yolo可以直接使用 train val test已经划分好 有yolov8训练200轮模型。 CrowdHuman 密集行人检测数据集 数据集描述 CrowdHuman数据集是一个专为密集行人检测设计的数据集&#xff0c;旨在解决行人密集场景下的检测挑…

【自动驾驶】控制算法(九)深度解析车辆纵向控制 | 从算法基础到 Carsim 仿真实践

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

【C语言】常见的C语言概念

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 什么是C语言2.C语言的历史3. 编译器的选择VS20223.1 编译和链接3.2 编译器的对比3.3 VS2022的优缺点 4.VS项目和源文件、头文件介绍5. 第一个C语言程序6. main函数7. printf和库函数8. 关键字介绍9. 字符和ASCII编…

【machine learning-十-梯度下降-学习率】

学习率 学习率不同的学习率 在梯度下降算法中&#xff0c;学习率的选择很重要&#xff0c;不恰当的选择&#xff0c;甚至可能导致损失发散&#xff0c;而非收敛&#xff0c;下面就看一下学习率的影响。 学习率 学习率是下图中的红框圈出来的部分&#xff0c; 学习率是模型的超…

Python 复制Excel 中的行、列、单元格

在Excel中&#xff0c;复制行、列和单元格是日常工作中经常需要进行的操作&#xff0c;它可以帮助你快速调整数据布局、复制数据模板或进行数据的批量处理。 本文将详细介绍如何使用Python将Excel中的行、列、或单元格范围复制到指定位置。 所需Python库 要使用Python操作Exc…

今天不写项目,聊聊后端面试吧

首先感谢大家之前的观看呀~兄弟们~ 这边把我去过几家公司面试的题目都写一下哈&#xff0c;像我大二下&#xff0c;就是前两个月7-9进了公司进行后端实习&#xff0c;哎.....反正就是学学学..话不多说~ 1.Frist 1.HashMap实现原理 HashMap是基于哈希表的Map接口的非同步实现…