java算法学习索引之动态规划

一 斐波那契数列问题的递归和动态规划

【题目】给定整数N,返回斐波那契数列的第N项。

补充问题 1:给定整数 N,代表台阶数,一次可以跨 2个或者 1个台阶,返回有多少种走法。

【举例】N=3,可以三次都跨1个台阶;也可以先跨2个台阶,再跨1个台阶;还可以先跨1个台阶,再跨2个台阶。所以有三种走法,返回3。

补充问题 2:假设农场中成熟的母牛每年只会生 1 头小母牛,并且永远不会死。第一年农场有1只成熟的母牛,从第二年开始,母牛开始生小母牛。每只小母牛3年之后成熟又可以生小母牛。给定整数N,求出N年后牛的数量。

【举例】N=6,第1年1头成熟母牛记为a;第2年a生了新的小母牛,记为b,总牛数为2;第3年a生了新的小母牛,记为c,总牛数为3;第4年a生了新的小母牛,记为d,总牛数为4。第5年b成熟了,a和b分别生了新的小母牛,总牛数为6;第6年c也成熟了,a、b和c分别生了新的小母牛,总牛数为9,返回9。

【要求】对以上所有的问题,请实现时间复杂度为O(logN)的解法。

斐波那契数列问题

奶牛问题

private int[][] multiMatrix(int[][] m1, int[][] m2) {//矩阵乘法// TODO Auto-generated method stubint[][] res=new int[m1.length][m2[0].length];for (int i = 0; i < m1.length; i++) {for (int j = 0; j < m2[0].length; j++) {for (int k = 0; k < m2.length; k++) {res[i][j]+=m1[i][k]*m2[k][j];}}}return res;
}public int f3(int n)
{if (n<1) {return 0;}if (n==1||n==2) {return 1;}int [][] base= {{1,1},{1,0}};int[][] res=matrixPower(base, n-2);return res[0][0]+res[1][0];
}public int c3(int n)
{if (n<1) {return 0;}if (n==1||n==2||n==3) {return 3;}int [][] base= {{1,0,1},{0,0,1},{1,0,0}};int[][] res=matrixPower(base, n-3);return 3*res[0][0]+2*res[1][0]+res[2][0];
}

二 矩阵的最小路径和

给定一个矩阵 m,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,返回所有的路径中最小的路径和。

经典动态规划方法。假设矩阵 m的大小为 M×N,行数为 M,列数为 N。先生成大小和 m一样的矩阵dp,dp[i][j]的值表示从左上角(即(0,0))位置走到(i,j)位置的最小路径和。对m的第一行的所有位置来说,即(0,j)(0≤j<N),从(0,0)位置走到(0,j)位置只能向右走,所以(0,0)位置到(0,j)位置的路径和就是 m[0][0..j]这些值的累加结果。同理,对 m 的第一列的所有位置来说,即(i,0) (0≤i<M),从(0,0)位置走到(i,0)位置只能向下走,所以(0,0)位置到(i,0)位置的路径和就是m[0..i][0]这些值的累加结果。

除第一行和第一列的其他位置(i,j)外,都有左边位置(i-1,j)和上边位置(i,j-1)。从(0,0)到(i,j)的路径必然经过位置(i-1,j)或位置(i,j-1),所以,dp[i][j]=min{dp[i-1][j],dp[i][j-1]}+m[i][j],含义是比较从(0,0)位置开始,经过(i-1,j)位置最终到达(i,j)的最小路径和经过(i,j-1)位置最终到达(i,j)的最小路径之间,哪条路径的路径和更小。那么更小的路径和就是 dp[i][j]的值。

public int minPathSum1(int[][] m) {if (m==null||m.length==0||m[0]==null||m[0].length==0) {return 0;}int row=m.length;int col=m[0].length;int[][] dp=new int[row][col];dp[0][0]=m[0][0];for (int i = 1; i < row; i++) {dp[i][0]=dp[i-1][0]+m[i][0];}for (int j = 0; j < col; j++) {dp[0][j]=dp[0][j-1]+m[0][j];}for (int i = 1; i < row; i++) {for (int j = 0; j < col; j++) {dp[i][j]=Math.min(dp[i-1][j], dp[i][j-1])+m[i][j];}}return dp[row-1][col-1];}

矩阵中一共有 M×N 个位置,每个位置都计算一次从(0,0)位置达到自己的最小路径和,计算的时候只是比较上边位置的最小路径和与左边位置的最小路径和哪个更小,所以时间复杂度为O(M×N),dp矩阵的大小为M×N,所以额外空间复杂度为O(M×N)。动态规划经过空间压缩后的方法。这道题的经典动态规划方法在经过空间压缩之后,时间复杂度依然是O(M×N),但是额外空间复杂度可以从O(M×N)减小至O(min{M,N}),也就是不使用大小为M×N的dp矩阵,而仅仅使用大小为min{M,N}的arr数组。具体过程如下

public int minPathSum2(int[][] m)
{if (m==null||m.length==0||m[0]==null||m[0].length==0) {return 0;}int more=Math.max(m.length, m[0].length);int less=Math.min(m.length, m[0].length);boolean rowmore= more==m.length;int[] arr=new int[less];arr[0]=m[0][0];for (int i = 1; i < less; i++) {arr[i]=arr[i-1]+(rowmore? m[0][i]:m[i][0]);//先求出到对角线的值}//数组  arr 中保存的是dp[i][i]中的值,但如果给定的矩阵行数小于列数(M<N),那么就生成长度为M的arr,然后令arr更新成dp矩阵每一列的值,及将arr 中的值保存为 dp[i][N]// 从左向右滚动过去for (int i = 1; i < more; i++) {arr[0]=arr[0]+(rowmore?m[i][0]:m[0][i]);for (int j = 1; j < arr.length; j++) {arr[j]=Math.min(arr[j-1], arr[j])+(rowmore?m[i][j]:m[j][i]);}}return arr[less-1];}

三 换钱的最少货币数

给定数组 arr,arr 中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim,代表要找的钱数,求组成aim的最少货币数。

方法一:暴力递归

public int minCoins1(int[] arr,int aim)
{if (arr==null||arr.length==0||aim<0) {return -1;}return process(arr,0,aim);
}private int process(int[] arr, int i, int rest) {if (i==arr.length) {return rest==0?0:-1;}int res=-1;for (int k = 0; k * arr[i]<=rest; k++) {int next=process(arr, i+1, rest-k*arr[i]);if (next!=-1) {res= res==-1?next+k:Math.min(res, next+k);}}return res;
}

//方法二:动态规划


public int minCoins(int[] arr, int aim) {
    if (arr == null || arr.length == 0 || aim < 0) {
        return -1; // 对于非法输入,返回-1
    }
    int N = arr.length;
    // 创建一个二维数组 dp,dp[i][j] 表示使用前 i 个货币组成金额 j 的最少货币数
    int[][] dp = new int[N + 1][aim + 1];
    
    // 初始化最后一行(i=N)为-1,表示使用最后一个货币无法组成任何金额
    for (int col = 1; col <= aim; col++) {
        dp[N][col] = -1;
    }
    
    // 从倒数第二行开始向第一行递推,计算每个单元格的值
    for (int i = N - 1; i >= 0; i--) {
        for (int rest = 0; rest <= aim; rest++) {
            dp[i][rest] = -1;   // 先将当前位置的值设为-1,表示无法组成目标金额
            
            // 如果使用下一行的值能够组成当前金额,直接继承下一行的值
            if (dp[i + 1][rest] != -1) {
                dp[i][rest] = dp[i + 1][rest];
            }
            
            // 如果当前面值可以被使用,并且使用当前面值可以组成剩余金额,
            // 则更新当前位置的值为使用当前面值和不适用当前面值两种情况中的最小值
            if (rest - arr[i] >= 0 && dp[i][rest - arr[i]] != -1) {
                if (dp[i][rest] == -1) {
                    dp[i][rest] = dp[i][rest - arr[i]] + 1;
                } else {
                    dp[i][rest] = Math.min(dp[i][rest], dp[i][rest - arr[i]] + 1);
                }
            }
        }
    }
    
    // 返回组成目标金额的最少货币数
    return dp[0][aim];
}
 

这段代码使用动态规划思想,通过填表的方式计算出组成目标金额所需的最少货币数。

minCoins2 方法就是填一张 N×aim 的表,而且因为省掉了枚举过程,所以每个位置的值都在O(1)的时间内得到,该方法时间复杂度为O(N×aim)。

四  机器人达到指定位置方法数

假设有排成一行的N个位置,记为1~N,N一定大于或等于2。开始时机器人在其中的M位置上(M一定是1~N中的一个),机器人可以往左走或者往右走,如果机器人来到1位置,那么下一步只能往右来到2位置;如果机器人来到N位置,那么下一步只能往左来到N-1位置。规定机器人必须走K步,最终能来到P位置(P也一定是1~N中的一个)的方法有多少种。给定四个参数N、M、K、P,返回方法数。

(1)
public int ways1(int N, int M, int K, int P) {
    // 检查输入参数的合法性
    if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {
        return 0; // 如果参数不合法,直接返回0
    }
    // 调用 walk 方法计算机器人在K步内走到位置P的方法数
    return walk(N, M, K, P);
}

private int walk(int N, int cur, int rest, int P) {
    if (rest == 0) {
        return cur == P ? 1 : 0; // 如果已经走完K步,检查是否到达位置P,是则返回1,否则返回0
    }
    if (cur == 1) {
        return walk(N, 2, rest - 1, P); // 如果当前位置是1,则只能往右走到位置2
    }
    if (cur == N) {
        return walk(N, N - 1, rest - 1, P); // 如果当前位置是N,则只能往左走到位置N-1
    }
    // 否则,计算能够往左或往右走到下一步位置的方法总数
    return walk(N, cur + 1, rest - 1, P) + walk(N, cur - 1, rest - 1, P);
}
 

这段代码使用递归的方式计算机器人在K步内走到位置P的方法数。walk 方法负责计算具体的步数和位置情况,其中使用了递归的方式进行计算。

(2)动态规划

public int ways2(int N, int M, int K, int P) {if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {return 0;}int[][] dp = new int[K + 1][N + 1];dp[0][P] = 1;for (int i = 1; i <= K; i++) {for (int j = 1; j <= N; j++) {if (j == 1) {dp[i][j] = dp[i - 1][2];} else if (j == N) {dp[i][j] = dp[i - 1][j - 1];} else {dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1];}}}return dp[K][M];
}

(3)动态规划+空间压缩


public int ways3(int N, int M, int K, int P) {
    if (N < 2 || K < 1 || M < 1 || M > N || P < 1 || P > N) {
        return 0; // 如果参数不合法,直接返回0
    }
    int[] dp = new int[N + 1]; // 创建一个长度为N+1的数组,用于保存每个位置的方法数
    dp[P] = 1; // 初始化目标位置P的方法数为1
    for (int i = 1; i <= K; i++) {
        int leftUp = dp[1]; // 保存上一行的左上角的值,即位置1的值
        for (int j = 1; j <= N; j++) {
            int tmp = dp[j]; // 保存当前位置的值
            if (j == 1) {
                dp[j] = dp[j + 1]; // 如果当前位置是1,只能往右走到位置2
            } else if (j == N) {
                dp[j] = leftUp; // 如果当前位置是N,只能往左走到位置N-1
            } else {
                dp[j] = leftUp + dp[j + 1]; // 否则,计算能够往左或往右走到下一步位置的方法总数
            }
            leftUp = tmp; // 更新左上角的值为上一行的当前位置的值
        }
    }
    return dp[M]; // 返回机器人在K步内走到位置M的方法数
}


这种解法利用动态规划,使用一维数组dp来记录每个位置的方法数。外层循环遍历K步,内层循环遍历每个位置,通过更新数组元素的方式计算每个位置的方法数。最后返回机器人在K步内走到位置M的方法数。

五  换钱的方法数

给定数组 arr,arr 中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim,代表要找的钱数,求换钱有多少种方法

(1)暴力解法

public int coins1(int[] arr, int aim) {if (arr == null || arr.length == 0 || aim < 0) {return 0;}return process1(arr, 0, aim);
}private int process1(int[] arr, int index, int aim) {// TODO Auto-generated method stubint res = 0;if (index == arr.length) {res = aim == 0 ? 1 : 0;} else {for (int i = 0; arr[index] * i <= aim; i++) {res += process1(arr, index + 1, aim - arr[index] * i);}}return res;}
(2) 记忆搜索的方法
public int coins2(int[] arr, int aim) {if (arr == null || arr.length == 0 || aim < 0) {return 0; // 如果数组为空或目标钱数小于0,直接返回0}int[][] map = new int[arr.length + 1][aim + 1]; // 创建一个二维数组,用于保存每个索引和钱数对应的换钱方法数return process2(arr, 0, aim, map); // 调用递归函数,并返回结果
}private int process2(int[] arr, int index, int aim, int[][] map) {int res = 0; // 定义结果变量resif (index == arr.length) { // 如果递归到达数组末尾res = aim == 0 ? 1 : 0; // 如果目标钱数为0,说明找到一种换钱方法,将res设为1,否则设为0} else {int mapValue = 0; // 用于存储之前计算过的换钱方法数for (int i = 0; arr[index] * i <= aim; i++) { // 遍历使用当前面值货币的数量mapValue = map[index + 1][aim - arr[index] * i]; // 查看之前计算过的换钱方法数if (mapValue != 0) { // 如果之前计算过res += mapValue == -1 ? 0 : mapValue; // 如果之前计算得到的是-1,说明之前的结果无效,不加入结果res中,否则加入结果res中} else { // 如果之前没有计算过res += process2(arr, index + 1, aim - arr[index] * i, map); // 继续递归计算剩余钱数的换钱方法数}}}map[index][aim] = res == 0 ? -1 : res; // 将计算得到的结果存入map数组中,若结果为0,则设为-1,表示无效结果return res; // 返回结果
}

段代码使用递归的方式解决换钱问题,并利用二维数组map来记录每个索引和钱数对应的换钱方法数。在递归函数process2中,首先判断递归是否到达了数组末尾,如果是,判断目标钱数是否为0,然后返回相应的结果。如果未到达数组末尾,则遍历使用当前面值货币的数量,并查看之前是否计算过对应的换钱方法数。如果计算过,将结果加入结果变量res中(若之前的结果为-1,说明无效,则不加入结果中),如果未计算过,则继续递归计算剩余钱数的换钱方法数。最后将计算得到的结果存入map数组中,并返回结果res。

public int coins3(int[] arr, int aim) {if (arr == null || arr.length == 0 || aim < 0) {return 0; // 如果数组为空或目标钱数小于0,直接返回0}int[][] dp = new int[arr.length][aim + 1]; // 创建一个二维数组dp,用于保存每个索引和钱数对应的换钱方法数for (int i = 0; i < arr.length; i++) {dp[i][0] = 1; // 当钱数为0时,方法数为1}for (int j = 1; arr[0] * j <= aim; j++) {dp[0][arr[0] * j] = 1; // 第一个面值的货币只能用来凑整数倍的自己,所以设置对应的方法数为1}int num = 0; // 用于存储临时的换钱方法数for (int i = 0; i < arr.length; i++) {for (int j = 1; j <= aim; j++) {num = 0; // 初始化临时变量为0for (int k = 0; j - arr[i] * k >= 0; k++) {num += dp[i - 1][j - arr[i] * k]; // 计算当前面值货币使用k张时的换钱方法数,并累加到临时变量中}dp[i][j] = num; // 将计算得到的结果存入dp数组中}}return dp[arr.length - 1][aim]; // 返回结果
}

这段代码使用动态规划解决换钱问题,并利用二维数组dp来记录每个索引和钱数对应的换钱方法数。首先初始化第一列,即钱数为0时的方法数都为1,因为总钱数为0时只有一种换钱方法,即什么都不换。然后初始化第一行,即第一个面值的货币只能用来凑整数倍的自己,所以设置对应的方法数为1。接下来,遍历数组中的每个面值货币和钱数,并根据状态转移方程计算当前面值货币和钱数对应的换钱方法数。最后返回dp数组的最后一个元素,即最终的结果。

可以优化为

public int coins4(int[] arr, int aim) {if (arr == null || arr.length == 0 || aim < 0) {return 0; // 如果数组为空或目标钱数小于0,直接返回0}int[][] dp = new int[arr.length][aim + 1]; // 创建一个二维数组dp,用于保存每个索引和钱数对应的换钱方法数for (int i = 0; i < arr.length; i++) {dp[i][0] = 1; // 当钱数为0时,方法数为1}for (int j = 1; arr[0] * j <= aim; j++) {dp[0][arr[0] * j] = 1; // 第一个面值的货币只能用来凑整数倍的自己,所以设置对应的方法数为1}for (int i = 0; i < arr.length; i++) {for (int j = 1; j <= aim; j++) {dp[i][j] = dp[i - 1][j]; // 初始化为上一个硬币的情况if (j - arr[i] >= 0) { // 判断当前面值的硬币是否可以组成jdp[i][j] += dp[i][j - arr[i]]; // 若可以组成,则加上组成j-arr[i]的情况数}}}return dp[arr.length - 1][aim]; // 返回结果
}

(4)空间压缩

public int coins5(int[] arr, int aim) {if (arr == null || arr.length == 0 || aim < 0) {return 0; // 如果数组为空或目标钱数小于0,直接返回0}int[] dp = new int[aim + 1]; // 创建一个一维数组dp,用于保存每个钱数对应的换钱方法数for (int j = 1; j <= aim; j++) {dp[arr[0] * j] = 1; // 第一个面值的货币只能用来凑整数倍的自己,所以设置对应的方法数为1}for (int i = 0; i < arr.length; i++) {for (int j = 1; j <= aim; j++) {dp[j] += j - arr[i] >= 0 ? dp[j - arr[i]] : 0; // 根据状态转移方程计算当前钱数对应的换钱方法数}}return dp[aim]; // 返回结果
}

这段代码使用动态规划解决换钱问题,并利用一维数组dp来记录每个钱数对应的换钱方法数。首先初始化第一个面值的货币的倍数对应的方法数为1。然后遍历数组中的每个面值货币和钱数,并根据状态转移方程计算当前钱数对应的换钱方法数。最后返回dp数组的最后一个元素,即最终的结果。这种方法优化了空间复杂度,只使用一维数组来保存动态规划的结果。

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

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

相关文章

python循环队列

1.循环队列简介&#xff1a; 循环队列是一种队列的实现方式&#xff0c;它可以避免队列空间的浪费。循环队列的特点是队列的末尾连接到队列的开头&#xff0c;形成一个循环。这样当队列尾部元素达到队列的最大容量时&#xff0c;新的元素可以循环地放入队列的开头。这种设计使…

Linux使用Docker完整安装Superset,同时解决please use superset_config.py to override it报错

文章目录 Docker安装Superset流程1. 首先获取镜像2. 生成SSL3. 创建Superset容器4. 更新数据库5. 测试访问Superset Docker安装Superset流程 1. 首先获取镜像 docker pull amancevice/superset2. 生成SSL 接下来我们运行一些额外的程序&#xff1a; openssl rand -base64 4…

ubuntu开机系统出错且无法恢复。请联系系统管理员。

背景&#xff1a; ubuntu22.04.2命令行&#xff0c;执行自动安装系统推荐显卡驱动命令&#xff0c;字体变大&#xff0c;重启后出现如下图错误&#xff0c;无法进入系统&#xff0c;无法通过CTRLALTF1-F3进入TTY模式。 解决办法&#xff1a; 1.首先要想办法进入系统&#xff…

python实现一个简单的桌面倒计时小程序

本章内容主要是利用python制作一个简单的桌面倒计时程序&#xff0c;包含开始、重置 、设置功能。 目录 一、效果演示 二、程序代码 一、效果演示 二、程序代码 #!/usr/bin/python # -*- coding: UTF-8 -*- """ author: Roc-xb """import tkin…

Python数据容器(字典)

字典 1.字典的定义2.字典数据的获取3.字典的嵌套4.嵌套字典的内容获取5.字典的常用操作6.常用操作总结7.遍历字典8.练习 1.字典的定义 同样使用{}&#xff0c;不过存储的元素是一个一个的&#xff1a;键值对&#xff0c;语法如下 # 定义字典字面量 {key:value,key:value,...,…

systemverilog:interface中的modport用法

使用modport可以将interface中的信号分组并指定方向&#xff0c;方向是从modport连接的模块看过来的。简单示例如下&#xff1a; interface cnt_if (input bit clk);logic rstn;logic load_en;logic [3:0] load;logic [7:0] count;modport TEST (input clk, count,output rst…

百度飞浆环境安装

前言&#xff1a; 在安装飞浆环境之前得先把pytorch环境安装好&#xff0c;不过关于pytorch网上教程最多的都是通过Anaconda来安装&#xff0c;但是Anaconda环境安装容易遇到安装超时导致安装失败的问题&#xff0c;本文将叫你如何通过pip安装的方式快速安装&#xff0c;其实这…

一键帮您解决win11最新版画图工具难用问题!

&#x1f984;个人主页:修修修也 ⚙️操作环境:Windows 11 正文 自从win11更新后,新版的画图工具变得非常难用,如: 使用橡皮擦后露出背版马赛克 框住某部分拖动移动时背景露出马赛克剪贴板上图片信息无法直接插入到画图板 目前没有一个好一些的能够在软件内部解决这些问题的方…

【nlp】2.1 认识RNN模型

认识RNN模型 1 什么是RNN模型2 RNN模型的作用3 RNN模型的分类:1 什么是RNN模型 RNN(Recurrent Neural Network),,中文称作循环神经网络,它一般以序列数据为输入, 通过网络内部的结构设计有效捕捉序列之间的关系特征,一般也是以序列形式进行输出。 一般单层神经网络结构:…

8.查询数据

一、单表查询 MySQL从数据表中查询数据的基本语为SELECT语。SELECT语的基本格式是: SELECT {* | <字段列名>} [ FROM <表 1>, <表 2>… [WHERE <表达式> [GROUP BY <group by definition> [HAVING <expression> [{<operator>…

Linux常用的磁盘使用情况命令汇总

1、查看分区使用百分比 df -h 2、查看指定目录磁盘使用情况 du -hac --max-depth1 /opt 参数&#xff1a;-a 查看所有文件&#xff0c;-c 汇总统计&#xff0c;max-depth1 查看深度为1&#xff0c;2级目录不再统计。 3、常用统计命令汇总

【vue实战项目】通用管理系统:api封装、404页

前言 本文为博主的vue实战小项目系列中的第三篇&#xff0c;很适合后端或者才入门的小伙伴看&#xff0c;一个前端项目从0到1的保姆级教学。前面的内容&#xff1a; 【vue实战项目】通用管理系统&#xff1a;登录页-CSDN博客 【vue实战项目】通用管理系统&#xff1a;封装to…

Java绘图-第19章

Java绘图-第19章 1.Java绘图类 1.1Graphics类 Graphics类是用于绘制图形的抽象类&#xff0c;它是java.awt包中的一部分。Graphics类提供了各种方法&#xff0c;可以在图形上绘制各种形状、文本和图像。这些方法包括画线、画矩形、画椭圆、画弧、绘制图像等。 1.2Graphics2…

Oracle(17)Managing Roles

目录 一、基础知识 1、基础介绍 2、Predefined Roles 预定义的角色 3、各种角色的介绍 二、基础操作 1、创建角色 2、修改用户默认role 3、回收role 4、删除role 5、为角色授权 6、授予角色给用户 7、查看用户包含的角色&#xff1a; 8、查看角色所包含的权限 9、…

reactive和effect,依赖收集触发依赖

通过上一篇文章已经初始化项目&#xff0c;集成了ts和jest。本篇实现Vue3中响应式模块里的reactive方法。 前置知识要求 如果你熟练掌握Map, Set, Proxy, Reflect&#xff0c;可直接跳过这部分。 Map Map是一种用于存储键值对的集合&#xff0c;并且能够记住键的原始插入顺…

React Virtual DOM及Diff算法

JSX到底是什么 使用React就一定会写JSX&#xff0c;JSX到底是什么呢&#xff1f;它是一种JavaScript语法的扩展&#xff0c;React使用它来描述用户界面长成什么样子&#xff0c;虽然它看起来非常像HTML&#xff0c;但他确实是javaScript&#xff0c;在React代码执行之前&#…

Kafka简单汇总

Kafka的结构图 多个Parttion共同组成这个topic的所有消息。每个consumer都属于一个consumer group&#xff0c;每条消息只能被consumer group中的一个Consumer消费&#xff0c; 但可以被多个consumer group消费。即组间数据是共享的&#xff0c;组内数据是竞争的。二、消费模型…

传输层协议-UDP协议

目录 传输层再谈端口号端口号范围划分认识知名端口号 UDP协议UDP协议格式UDP数据封装UDP数据分用 UDP协议的特点面向数据报 UDP缓冲区UDP使用注意事项基于UDP的应用层协议 传输层 实际上我们应用层的数据并不是直接发给网络的&#xff0c;而是需要先将数据发送给传输层&#xf…

指针传2

几天没有写博客了&#xff0c;怎么说呢&#xff1f;这让我总感觉缺点什么&#xff0c;心里空落落的&#xff0c;你懂吧&#xff01; 好了&#xff0c;接下来开始我们今天的正题&#xff01; 1. ⼆级指针 我们先来看看代码&#xff1a; 首先创建了一个整型变量a&#xff0c;将…

一题带你写出图论算法模板!!!

这题是道基础的图论算法题目 注释很重要&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 在做这道题之前&#xff0c;我们先了解一下基础的图论算法吧&#xff01;&#xff01;&#xff01; 1.floyd&#xff1a; 这样可以求出所有点…