算法通关村第十九关——动态规划高频问题(白银)

news2025/1/16 13:48:03

算法通关村第十九关——动态规划高频问题(白银)

    • 前言
    • 1 最少硬币数
    • 2 最长连续递增子序列
    • 3 最长递增子序列
    • 4 完全平方数
    • 5 跳跃游戏
    • 6 解码方法
    • 7 不同路径 II

前言

摘自:代码随想录

动态规划五部曲:

  1. 确定dp数组(dp table)及其下标的含义
  2. 确定递推公式
  3. 初始化dp数组
  4. 确定遍历顺序
  5. 举例推导dp数组

1 最少硬币数

leetcode 322. 零钱兑换

动规五部曲分析如下:

  1. 确定dp数组以及下标的含义

dp[j]:凑足总额为 j 所需钱币的最少个数为dp[j]

  1. 确定递推公式

凑足总额为j - coins[i]的最少个数为dp[j - coins[i]],

那么只需要加上一个钱币coins[i]即dp[j - coins[i]] + 1就是dp[j](考虑coins[i])

所以dp[j] 要取所有 dp[j - coins[i]] + 1 中最小的。

递推公式:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);

  1. dp数组如何初始化

首先凑足总金额为0所需钱币的个数一定是0,那么dp[0] = 0;

其他下标对应的数值呢?

考虑到递推公式的特性,dp[j]必须初始化为一个最大的数,否则就会在min(dp[j - coins[i]] + 1, dp[j])比较的过程中被初始值覆盖。

所以下标非0的元素都是应该是最大值。

int[] dp = new int[amount + 1];
// 往数组dp里面填充某个数,这里选择amount+1,就是最大的值
Arrays.fill(dp, amount+1);
dp[0] = 0;
  1. 确定遍历顺序

有两种方式:

第一种:外循环遍历金额,内循环遍历硬币面额。

第二种:外循环遍历硬币面面额,内循环遍历金额。

这两种遍历顺序对应的意义如下:

  1. 外循环遍历金额,内循环遍历硬币面额:

    这种遍历顺序的意义是在计算找零过程中,我们首先考虑金额的变化,然后再考虑不同的硬币面额。

    也就是说,我们固定一个金额,尝试使用不同的硬币面额来找零。这样做的好处是可以利用之前已经计算出来的金额的最少硬币数,快速得到当前金额的最优解。由于金额是从小到大递增的,所以我们在计算每个金额的最优解时,可以利用前面较小金额的最优解已经被计算出来的特点。

// 遍历金额
for (int i = 1; i <= amount; i++) {
    // 遍历硬币面额
    for (int j = 0; j < coins.length; j++) {
        if (coins[j] <= i) {
            dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
        }
    }
}
  1. 外循环遍历硬币面额,内循环遍历金额:

    这种遍历顺序的意义是在计算找零过程中,我们首先考虑不同的硬币面额,然后再考虑不同的金额。

    也就是说,我们固定一个硬币面额,尝试在不同的金额下进行找零。这样做的好处是可以保证我们将所有可能的硬币面额都考虑到,并且在计算每个金额的最优解时,可以利用之前已经计算出来的较小金额的最优解。由于硬币面额是从小到大递增的,所以我们在计算每个金额的最优解时,可以利用之前较小硬币面额的最优解已经被计算出来的特点。

// 遍历硬币面额
for (int coin : coins){
    // 遍历金额
    for (int i = 1; i <= amount; i++) {
        if(coin <= i){
            dp[i] = Math.min(dp[i], dp[i - coin] + 1);
        }
    }
}

全部代码如下:

第一种:

class Solution {
    public int coinChange(int[] coins, int amount) {
        // 初始化dp数组
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        dp[0] = 0;

        // 遍历金额
        for (int i=1; i <= amount; i++) {
            // 遍历硬币面额
            for (int j=0; j < coins.length; j++){
                if(coins[j] <= i){
                    dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
                }
            }
        }
            
            
        return dp[amount] > amount ? -1 : dp[amount];
    }
}

第二种:

class Solution {
    public int coinChange(int[] coins, int amount) {
        // 初始化dp数组
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        dp[0] = 0;

        // 遍历硬币面额
        for (int coin : coins){
            // 遍历金额
            for (int i = 1; i <= amount; i++) {
                if(coin <= i){
                    dp[i] = Math.min(dp[i], dp[i - coin] + 1);
                }
            }
        }
            
        return dp[amount] > amount ? -1 : dp[amount];
    }
}

2 最长连续递增子序列

leetcode 674. 最长连续递增序列

动规五部曲分析如下:

  1. 确定dp数组以及下标的含义

dp数组:表示以当前元素为结尾的最长连续递增序列的长度。

dp[i]表示以nums[i]为结尾的最长连续递增序列的长度。

  1. 确定递推公式

如果nums[i] > nums[i-1],则dp[i] = dp[i-1] + 1;否则dp[i] = 1。

  1. dp数组如何初始化

我们将dp数组的所有元素初始化为1,因为每个元素都可以作为一个单独的递增序列。

  1. 确定遍历顺序

从第二个元素开始遍历:

for(int i=0; i < nums.length; i++){
    if(i > 0 && nums[i] > nums[i-1]){
        dp[i] = dp[i-1] + 1;
    }else{
        dp[i] = 1;
    }
}
  1. 举例说明

举例说明:给定数组nums = [1, 3, 5, 4, 7]。

遍历过程如下:

  • 对于nums[1] = 3,nums[0] = 1 < nums[1],所以dp[1] = dp[0] + 1 = 2。
  • 对于nums[2] = 5,nums[1] = 3 < nums[2],所以dp[2] = dp[1] + 1 = 3。
  • 对于nums[3] = 4,nums[2] = 5 > nums[3],所以dp[3] = 1。
  • 对于nums[4] = 7,nums[3] = 4 < nums[4],所以dp[4] = dp[3] + 1 = 2。

最终的最长连续递增序列的长度为dp数组的最大值,即为3。

最后代码如下:

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }

        int[] dp = new int[nums.length];
        dp[0] = 1;

        for(int i=1; i < nums.length; i++){
            if(nums[i] > nums[i-1]){
                dp[i] = dp[i-1] + 1;
            }else{
                dp[i] = 1;
            }
        }

        return Arrays.stream(dp).max().getAsInt();
    }
}

不是使用stream的方式:

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }

        int[] dp = new int[nums.length];
        dp[0] = 1;
        int maxLength = 1;

        for(int i=1; i < nums.length; i++){
            if(nums[i] > nums[i-1]){
                dp[i] = dp[i-1] + 1;
            }else{
                dp[i] = 1;
            }
            maxLength = Math.max(maxLength, dp[i]);
        }

        return maxLength;
    }
}

还可以得到dp[i],再遍历一遍得到最大值,这就不写了

3 最长递增子序列

leetcode 300. 最长递增子序列

  1. 确定dp数组(dp table)及其下标的含义:

    • dp数组:dp[i] 表示以第i个数字结尾的最长递增子序列的长度。
    • 下标的含义:dp[i] 表示以第i个数字结尾的最长递增子序列的长度。
  2. 确定递推公式:

    • 如果nums[i] > nums[j],则:dp[i] = max(dp[i], dp[j] + 1)。

    为啥呢??

    这里的i和j表示数组nums的索引。具体来说,i表示当前遍历到的元素的索引,而j表示在i之前的元素的索引。

    当我们遍历到第i个元素时,我们需要寻找在i之前的元素中比nums[i]小的元素。这样,我们就可以利用这个小于nums[i]的元素来构成一个更长的递增子序列。

    所以,当nums[i] > nums[j]时,表示nums[i]比nums[j]大,我们可以将以j结尾的最长递增子序列的长度加1,然后与以i结尾的最长递增子序列的长度进行比较,取较大的值作为以i结尾的最长递增子序列的长度。也就是递推公式中的 dp[i] = max(dp[i], dp[j] + 1)

  3. 初始化dp数组:

    • 初始时,dp数组中的每个元素都设为1,因为最短的递增子序列长度为1。
  4. 确定遍历顺序:

    • 外层循环遍历数组nums,从左到右依次计算dp[i]的值。
    • 内层循环遍历数组nums,从数组开始到i的位置,寻找前面的数字nums[j]是否小于nums[i],如果是,则根据递推公式更新dp[i]的值。
  5. 举例推导dp数组:

    如果nums[i] > nums[j],则dp[i] = max(dp[i], dp[j] + 1)。

    逐个元素计算dp[i]的值:

    • 当i = 1时,nums[i] = 9,此时没有比9小的元素,所以以9结尾的最长递增子序列长度仍为1。

    nums: 10 9 2 5 3 7 101 18
    dp: 1 1 1 1 1 1 1 1

    • 当i = 2时,nums[i] = 2,此时在2之前有9和10两个元素,都比2大,所以以2结尾的最长递增子序列长度仍为1。

    nums: 10 9 2 5 3 7 101 18
    dp: 1 1 1 1 1 1 1 1

    • 当i = 3时,nums[i] = 5,此时在5之前有2和9两个元素,其中2比5小,所以以5结尾的最长递增子序列长度为dp[2] + 1 = 2。

    nums: 10 9 2 5 3 7 101 18
    dp: 1 1 1 2 1 1 1 1

    • 当i = 4时,nums[i] = 3,此时在3之前有2和5两个元素,其中2比3小,所以以3结尾的最长递增子序列长度为dp[2] + 1 = 2。

    nums: 10 9 2 5 3 7 101 18
    dp: 1 1 1 2 2 1 1 1

后面略~~~~~~

完整代码如下:

class Solution {
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        
        int n = nums.length;
        int[] dp = new int[n];
        dp[0] = 1; 
        
        int result = 1; 
        for (int i = 1; i < n; i++) {
            dp[i] = 1; 
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) { 
                    dp[i] = Math.max(dp[i], dp[j] + 1); 
                }
            }
            
            result = Math.max(result, dp[i]); 
        }
        
        return result;
    }
}

4 完全平方数

leetcode 279. 完全平方数

动态规划五部曲:

  1. 确定dp数组(dp table)及其下标的含义

**dp[i]:**表示数字i的最少完全平方数的个数。

  1. 确定递推公式

对于数字 i 来说,我们需要遍历所有小于等于 i 的完全平方数 j( j 从 1 到 sqrt(i) ),然后将当前数字 i 减去 j 得到差值,即 i - j 。我们需要找到 dp[ i - j * j ] 的最小值,然后再加上1(表示当前完全平方数 j ),即可得到dp[i]的值。

递推公式为:dp[i] = Math.min(dp[i], dp[i - j * j] + 1),其中 j * j <= i。

  1. 初始化dp数组

Arrays.fill(dp, Integer.MAX_VALUE);

dp[0] = 0;

  1. 确定遍历顺序
// 遍历dp数组
for (int i = 1; i <= n; i++) {
    // 遍历小于等于i的完全平方数j*j
    for (int j = 1; j * j <= i; j++) {
        // 更新dp[i]
        dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
    }
}
  1. 举例推导dp数组

略。。。

完整代码:

class Solution {
    public int numSquares(int n) {
       // 定义dp数组
        int[] dp = new int[n + 1];
        // 初始化dp数组
        Arrays.fill(dp, n+1);
        dp[0] = 0;

        // 遍历dp数组
        for (int i = 1; i <= n; i++) {
            // 遍历小于等于i的完全平方数j*j
            for (int j = 1; j * j <= i; j++) {
                // 更新dp[i]
                dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
            }
        }

        return dp[n]; 
    }
}

当然,这个代码可以再优化一下:(使用Math的api)
减少内层循环的次数:对于小于等于 i 的完全平方数 j ,我们可以通过计算 i - j * j 的平方根得到 j 的最大值,并从最大值开始遍历,这样可以减少内层循环的次数。

class Solution {
    public static int numSquares(int n) {
        // 定义dp数组
        int[] dp = new int[n + 1];
        // 初始化dp数组
        Arrays.fill(dp, n + 1);
        dp[0] = 0;

        // 遍历dp数组
        for (int i = 1; i <= n; i++) {
            // 获取当前数字i的最大完全平方数j*j
            int maxSquare = (int) Math.sqrt(i);
            // 遍历完全平方数j*j
            for (int j = maxSquare; j >= 1; j--) {
                // 更新dp[i]
                dp[i] = Math.min(dp[i], dp[i - j * j] + 1);
            }
        }

        return dp[n];
    }
}

5 跳跃游戏

leetcode 55. 跳跃游戏

动态规划五部曲:

  1. 确定dp数组(dp table)及其下标的含义

dp[i]表示从起点位置到达位置i时能否跳跃到最后一个位置。

  1. 确定递推公式

dp[i] = (dp[j] && nums[j] >= i - j),其中0 <= j < i

  1. 初始化dp数组

初始化dp数组所有位置为false。

  1. 确定遍历顺序

外层循环遍历i从1到n-1,内层循环遍历j从0到i-1。

  1. 举例推导dp数组

以数组nums = [2, 3, 1, 1, 4]为例进行推导:

初始状态:
dp = [false, false, false, false, false]

推导dp[1]:
dp[1] = (dp[0] && nums[0] >= 1 - 0) = (false && 2 >= 1) = false

推导dp[2]:
dp[2] = (dp[0] && nums[0] >= 2 - 0) || (dp[1] && nums[1] >= 2 - 1) = (false && 2 >= 2) || (false && 3 >= 2) = false

推导dp[3]:
dp[3] = (dp[0] && nums[0] >= 3 - 0) || (dp[1] && nums[1] >= 3 - 1) || (dp[2] && nums[2] >= 3 - 2) = (false && 2 >= 3) || (false && 3 >= 3) || (false && 1 >= 3) = false

完整代码如下:

class Solution {
    public boolean canJump(int[] nums) {
        // 获取数组长度
        int n = nums.length;
        // 定义dp数组
        boolean[] dp = new boolean[n];
        // 初始化dp数组
        dp[0] = true;

        // 遍历dp数组
        for (int i = 1; i < n; i++) {
            // 内层循环遍历j
            for (int j = 0; j < i; j++) {
                // 更新dp[i]
                dp[i] = dp[j] && nums[j] >= i - j;
                // 如果dp[i]为true,则跳出内层循环
                if (dp[i]) {
                    break;
                }
            }
        }

        return dp[n - 1];
    }
}

6 解码方法

leetcode 91. 解码方法

动态规划五部曲:

  1. 确定dp数组(dp table)及其下标的含义

dp[i]表示从字符串的起始位置到第i个字符时的解码方法总数。

  1. 确定递推公式

对于dp数组中的每个位置i,我们需要考虑两个情况:

  • 如果第i个字符能够单独解码(即不为0),则dp[i] = dp[i-1],因为第i个字符自身可以作为一个解码方法;
  • 如果第i个字符与前一个字符组成的两位数能够解码(即与前一个字符组成的数字在1到26之间),则dp[i] += dp[i-2],因为组成的两位数可以作为一个解码方法。

则,递推公式为:dp[i] = dp[i-1] + dp[i-2],其中0 <= i < n。

  1. 初始化dp数组

初始化dp数组的长度为n+1,初始值为0。

  1. 确定遍历顺序
for (int i = 1; i <= n; i++) {
    // 如果第i个字符能够单独解码(即不为0)
    if (s.charAt(i - 1) != '0') {
        dp[i] += dp[i - 1];
    }
    // 如果第i个字符与前一个字符组成的两位数能够解码(即与前一个字符组成的数字在1到26之间)
    if (i >= 2 && isValidEncoding(s.substring(i - 2, i))) {
        dp[i] += dp[i - 2];
    }
}
// 判断字符串编码是否在1到26之间
private static boolean isValidEncoding(String s) {
    if (s.charAt(0) == '0') {
        return false;
    }
    int num = Integer.parseInt(s);
    return num >= 1 && num <= 26;
}
  1. 举例推导dp数组

以字符串s = "226"为例进行推导:

初始状态:
dp = [1, 0, 0, 0]

推导dp[1]:
如果第1个字符为2,能够单独解码为"2",所以dp[1] = dp[0] = 1

推导dp[2]:
如果第2个字符为2,能够单独解码为"2",所以dp[2] = dp[1] = 1
如果第1个字符与第2个字符组成的两位数为26,能够解码为"26",所以dp[2] += dp[0],即dp[2] = dp[1] + dp[0] = 1 + 1 = 2

推导dp[3]:
如果第3个字符为6,能够单独解码为"6",所以dp[3] = dp[2] = 2
如果第2个字符与第3个字符组成的两位数为26,能够解码为"26",所以dp[3] += dp[1],即dp[3] = dp[2] + dp[1] = 2 + 1 = 3

最终结果:
dp = [1, 1, 2, 3]

完整代码:

class Solution {
    public static int numDecodings(String s) {
        // 获取字符串的长度
        int n = s.length();
        // 定义dp数组
        int[] dp = new int[n + 1];
        // 初始化dp数组
        dp[0] = 1;

        // 遍历dp数组
        for (int i = 1; i <= n; i++) {
            // 如果第i个字符能够单独解码(即不为0)
            if (s.charAt(i - 1) != '0') {
                dp[i] += dp[i - 1];
            }
            // 如果第i个字符与前一个字符组成的两位数能够解码(即与前一个字符组成的数字在1到26之间)
            if (i >= 2 && isValidEncoding(s.substring(i - 2, i))) {
                dp[i] += dp[i - 2];
            }
        }

        return dp[n];
    }

    // 判断字符串编码是否在1到26之间
    private static boolean isValidEncoding(String s) {
        if (s.charAt(0) == '0') {
            return false;
        }
        int num = Integer.parseInt(s);
        return num >= 1 && num <= 26;
    }
}

不过可以简化一下,就是比较难理解一点,意义一样滴:

class Solution {
    public int numDecodings(String s) {
        int n = s.length();
        int[] f = new int[n + 1];
        f[0] = 1;
        for (int i = 1; i <= n; ++i) {
            if (s.charAt(i - 1) != '0') {
                f[i] += f[i - 1];
            }
            if (i > 1 && s.charAt(i - 2) != '0' 
                && ((s.charAt(i - 2) - '0') * 10 + (s.charAt(i - 1) - '0') <= 26)) {
                f[i] += f[i - 2];
            }
        }
        return f[n];
    }
}

7 不同路径 II

leetcode 63. 不同路径 II

这题就是62的改版,所以复杂了很多,还是建议看代码随想录:动态规划——不同路径

动规五部曲:

  1. 确定dp数组(dp table)以及下标的含义

**dp[i] [j] :**表示从(0 ,0)出发,到(i, j) 有dp[i] [j]条不同的路径。

  1. 确定递推公式

递推公式和62.不同路径一样,dp[i] [j] = dp[i - 1] [j] + dp[i] [j - 1]。

但这里需要注意一点,因为有了障碍,(i, j)如果就是障碍的话应该就保持初始状态(初始状态为0)。

所以代码为:

if (obstacleGrid[i][j] == 0) { // 当(i, j)没有障碍的时候,再推导dp[i][j]
    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
  1. dp数组如何初始化

因为从(0, 0)的位置到(i, 0)的路径只有一条,所以dp[i] [0]一定为1,dp[0] [j]也同理。

但如果(i, 0) 这条边有了障碍之后,障碍之后(包括障碍)都是走不到的位置了,所以障碍之后的dp[i] [0]应该还是初始值0。

如图:

image-20230910161750355

下标(0, j)的初始化情况同理。

所以本题初始化代码为:

int[][] dp = new int[m][n];
for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) {
    dp[i][0] = 1;
}
for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) {
    dp[0][j] = 1;
}

注意代码里for循环的终止条件,一旦遇到obstacleGrid[i] [0] == 1的情况就停止dp[i] [0]的赋值1的操作,dp[0] [j]同理

  1. 确定遍历顺序

从递归公式dp[i] [j] = dp [i - 1] [j] + dp[i] [j - 1] 中可以看出,一定是从左到右一层一层遍历,这样保证推导dp[i] [j]的时候,dp[i - 1] [j] 和 dp[i] [j - 1]一定是有数值。

代码如下:

for (int i = 1; i < m; i++) {
    for (int j = 1; j < n; j++) {
        dp[i][j] = (obstacleGrid[i][j] == 0) ? dp[i - 1][j] + dp[i][j - 1] : 0;
    }
}
  1. 举例推导dp数组

image-20230910162011436

完整代码如下:

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];

        //如果在起点或终点出现了障碍,直接返回0
        if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1) {
            return 0;
        }

        for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) {
            dp[i][0] = 1;
        }
        for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) {
            dp[0][j] = 1;
        }

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                dp[i][j] = (obstacleGrid[i][j] == 0) ? dp[i - 1][j] + dp[i][j - 1] : 0;
            }
        }
        return dp[m - 1][n - 1];
    }
}

至于118,119我个人觉得并不合适使用动态规划的方式,所以就不写了,over~~

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

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

相关文章

Unicode:Codejock Suite Prov22.1.0 for ActiveX Crack

Codejock 荣获 2022 年 Visual Studio 杂志读者选择奖&#xff01;Visual Studio 杂志宣布了第 28 届年度读者选择奖的获奖者。今年是 Visual Studio 杂志的读者对其 Visual Studio 相关工具和服务进行投票的第 28 年&#xff0c;他们对 42 个类别的产品提供了意见&#xff0c;…

Windows开启linux子系统并迁移到非系统盘

windows用户如想使用linux有一个非常简单的方法&#xff0c;就是开启windows下的linux子系统&#xff0c;也叫做WSL(Windows Subsystem for Linux)&#xff0c;下面简述一下安装过程。 首先打开控制面板&#xff0c;程序功能下面的开启或关闭windows功能 然后再下面这个界面勾…

【C刷题】day1

一、选择题 1.正确的输出结果是 int x5,y7; void swap() { int z; zx; xy; yz; } int main() { int x3,y8; swap(); printf("%d,%d\n"&#xff0c;x, y); return 0; } 【答案】&#xff1a; 3&#xff0c;8 【解析】&#xff1a; 考点&#xff1a; &#xff…

Redis使用原生命令搭建集群

1.Redis版本及下载 找到安装的redis版本&#xff0c;redis3.0以上版本才支持集群 下载对应的版本 2.安装redis集群 解压上传编译 [hadoophost152 opensource]$ tar -xvf redis-3.2.11.tar.gz [hadoophost152 opensource]$ cd redis-3.2.11/ [hadoophost152 redis-3.2.11]$ ma…

数据结构 每日一练:将带头结点的单链表就地逆置(视频讲解两种方法)

目录 方法一 算法视频分析 方法二 算法视频分析 Q&#xff1a;什么是“就地”捏&#xff1f; A&#xff1a;就是指辅助空间复杂度为O(1)&#xff0c;通俗一点来说就是不需要再开辟一块空间来实现算法。 特别说明&#xff1a; 笔者第一次录制视频&#xff0c;言语有些不顺&…

支持事务的分布式NoSQL——FoundationDB

【引子】周末阅读时光&#xff0c;一篇好的论文&#xff08;https://cacm.acm.org/magazines/2023/6/273229-foundationdb-a-distributed-key-value-store/fulltext&#xff09;&#xff0c;开阔了眼界&#xff0c;支持事务语义的NoSQL应该放到软件系统架构备选方案之中。 Foun…

Kotlin 协程 - 生命周期 Job

一、概念 对于每一个由协程构建器开启的协程&#xff0c;都会返回一个 Job 实例用来管理协程的生命周期。launch()直接返回 Job实现&#xff0c;async() 返回的 Deferred 实现了 Job接口。 Job public fun start(): Boolean public fun cancel(cause: CancellationException? …

java中HashMap如何根据value的值去获取key是多少

在Java中&#xff0c;HashMap是一种基于键值对存储数据的数据结构。HashMap并没有直接提供根据value获取key的方法。但你可以通过遍历HashMap的entrySet&#xff0c;找到对应的value&#xff0c;然后获取其对应的key。 以下是一个示例代码&#xff1a; public <K, V> K…

计算机图形学环境配置java3D

计算机图形学环境配置java3D JDK18&#xff08;或者一些版本都无法支持Applet类&#xff09;idea配置导入java3D的jar包测试代码&#xff1a;运行效果&#xff1a; java3Dwindows64位下载 这个是默认到下图路径中&#xff1a;&#xff08;记住这个路径&#xff0c;待会要导入ja…

2023Web前端开发面试手册

​​​​​​​​ HTML基础 1. HTML 文件中的 DOCTYPE 是什么作用&#xff1f; HTML超文本标记语言: 是一个标记语言, 就有对应的语法标准 DOCTYPE 即 Document Type&#xff0c;网页文件的文档类型标准。 主要作用是告诉浏览器的解析器要使用哪种 HTML规范 或 XHTML规范…

【C++笔记】C++STL vector类模拟实现

【C笔记】CSTL vector类模拟实现 一、实现模型和基本接口1.1、各种构造和析构1.2、迭代器 二、各种插入和删除接口2.1、插入接口2.1、删除接口2.3、resize接口 三、运算符重载3.1、方括号运算符重载3.2、赋值运算符重载 一、实现模型和基本接口 实现模型我们选择模拟库中的模型…

企业架构LNMP学习笔记35

学习目标和内容&#xff1a; 1、能够通过HAproxy实现负载均衡。 2、安装&#xff1a; yum install -y haproxy 3、配置文件修改点&#xff1a; 修改为80&#xff0c;并将后面几个用不到的&#xff0c;都进行删除。 代理转发到后端的app端。 4、后端app端的定义&#xff1b; …

第10章_瑞萨MCU零基础入门系列教程之中断控制单元简介

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…

【图论】SPFA求负环

算法提高课笔记 文章目录 基础知识例题虫洞题意思路代码 观光奶牛题意思路代码 单词环题意思路代码 基础知识 负环&#xff1a;环上权值之和是负数 求负环的常用方法 基于SPFA 统计每个点入队次数&#xff0c;如果某个点入队n次&#xff0c;则说明存在负环&#xff08;完全…

OSPF路由计算

1、Router LSA LSA 链路状态通告&#xff0c;是OSPF进行路由计算的主要依据&#xff0c;在OSPF的LSU报文中携带&#xff0c;其头重要字段及解释&#xff1a; LS Type&#xff08;链路状态类型&#xff09;&#xff1a;指示本LSA的类型。 在域内、域间、域外…

upload-labs/Pass-07 未知后缀名解析漏洞复现

upload-labs/Pass-07 漏洞复现 页面&#xff1a; 我们看到有一个图片上传功能。 我们上传一个png文件发现能够成功上传&#xff0c;那其他文件呢&#xff0c;如php文件。 我们看一下是否能上传一个php文件&#xff1a; php文件内容&#xff1a; <?phpeval($_REQUEST[]…

计算机系统的基本概念

计算机系统的基本概念 本文主要以hello.c这个程序的整个生命周期来简单了解一下计算机系统结构的基本概念。 #include <stdio.h>int main() {printf("hello, world\n");return 0; }gcc hello.c -o hello ./hello hello, world此刻&#xff0c;hello.c源程序…

运算符,switch

目录 算术运算符 逻辑运算符 强制类型转换 自增自减运算符 ​编辑 三目运算符 A&#xff1f;B:C 逗号表达式 switch 算术运算符 除法的运算结果和运算对象的数据类型有关&#xff0c;两个都是int商就是int&#xff0c;被除数或者除数只要有一个是浮点型数据&#xff0c;…

ARM DIY(十一)板子名称、开机 logo、LCD 控制台、console 免登录、命令提示符、文件系统大小

文章目录 前言板子名称uboot Modelkernel 欢迎词、主机名 开机 logoLCD 控制台console 免登录命令提示符文件系统大小 前言 经过前面十篇文章的介绍&#xff0c;硬件部分调试基本完毕&#xff0c;接下来的文章开始介绍软件的个性化开发。 板子名称 uboot Model 既然是自己的…

最新2米分辨率北极开源DEM数据集(矢量文件)

一、项目背景 美国明尼苏达大学(University of Minnesota)的极地地理空间中心(Polar Geospatial Center, PGC)于2023年8月发布了北极数字高程模型4.1版本(ArcticDEM Mosaic 4.1)。该DEM数据集是革命性的&#xff0c;分辨率达到了2米&#xff0c;而一般的开源DEM数据集分辨率是3…