子数组、子串系列(典型算法思想)—— OJ例题算法解析思路

news2025/3/10 9:53:15

一、53. 最大子数组和 - 力扣(LeetCode)

算法代码: 

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        // 1. 创建 dp 表
        // dp[i] 表示以第 i 个元素结尾的子数组的最大和
        int n = nums.size();
        vector<int> dp(n + 1); // 定义大小为 n+1 的 dp 表

        // 2. 初始化
        int ret = INT_MIN; // 用于记录全局最大值,初始化为最小整数值

        // 3. 填表
        for (int i = 1; i <= n; i++) {
            // 状态转移方程:
            // dp[i] = max(单独作为子数组, 加入前面的子数组)
            dp[i] = max(nums[i - 1], dp[i - 1] + nums[i - 1]);

            // 更新全局最大值
            ret = max(ret, dp[i]);
        }

        // 4. 返回结果
        return ret; // 返回全局最大值
    }
};

代码思路

  1. 问题分析

    • 目标是找到数组中连续子数组的最大和。

    • 使用动态规划来解决,定义dp[i]表示以第i个元素结尾的子数组的最大和。

  2. 状态定义

    • dp[i]:以第i个元素结尾的子数组的最大和。

  3. 状态转移方程

    • 对于第i个元素,有两种选择:

      • 单独作为一个子数组:nums[i-1]

      • 加入前面的子数组:dp[i-1] + nums[i-1]

    • 取两者的最大值:dp[i] = max(nums[i-1], dp[i-1] + nums[i-1])

  4. 初始化

    • dp[0]没有意义,可以忽略。

    • dp[1]表示以第1个元素结尾的子数组的最大和,即nums[0]

  5. 结果

    • 遍历dp表,找到最大值。


代码逻辑详解

  1. 创建 DP 表

    • int n = nums.size();

      • 获取数组的长度n

    • vector<int> dp(n + 1);

      • 定义一个大小为n+1的数组dp,用于存储以每个位置结尾的子数组的最大和。

  2. 初始化

    • int ret = INT_MIN;

      • 初始化ret为最小整数值,用于记录全局最大值。

  3. 填表

    • for (int i = 1; i <= n; i++)

      • 遍历数组的每个元素,i表示当前元素的位置(从1开始)。

    • dp[i] = max(nums[i - 1], dp[i - 1] + nums[i - 1]);

      • 计算以第i个元素结尾的子数组的最大和:

        • nums[i-1]:单独作为子数组。

        • dp[i-1] + nums[i-1]:加入前面的子数组。

      • 取两者的最大值。

    • ret = max(ret, dp[i]);

      • 更新全局最大值ret

  4. 返回结果

    • return ret;

      • 返回全局最大值。


示例

假设输入数组为:nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]

执行过程:

  1. 初始化:

    • dp = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

    • ret = INT_MIN

  2. 填表:

    • i=1dp[1] = max(-2, 0 + -2) = -2ret = max(INT_MIN, -2) = -2

    • i=2dp[2] = max(1, -2 + 1) = 1ret = max(-2, 1) = 1

    • i=3dp[3] = max(-3, 1 + -3) = -2ret = max(1, -2) = 1

    • i=4dp[4] = max(4, -2 + 4) = 4ret = max(1, 4) = 4

    • i=5dp[5] = max(-1, 4 + -1) = 3ret = max(4, 3) = 4

    • i=6dp[6] = max(2, 3 + 2) = 5ret = max(4, 5) = 5

    • i=7dp[7] = max(1, 5 + 1) = 6ret = max(5, 6) = 6

    • i=8dp[8] = max(-5, 6 + -5) = 1ret = max(6, 1) = 6

    • i=9dp[9] = max(4, 1 + 4) = 5ret = max(6, 5) = 6

  3. 返回结果:6


总结

  • 问题本质:动态规划解决最大子数组和问题。

  • 核心思想:通过状态转移方程计算以每个位置结尾的子数组的最大和,并记录全局最大值。

  • 时间复杂度O(n),遍历数组一次。

  • 空间复杂度O(n),使用了一个大小为n+1dp表。可以通过滚动变量优化为O(1)

二、918. 环形子数组的最大和 - 力扣(LeetCode) 

算法代码:

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        // 1. 创建 dp 表
        // f[i] 表示以第 i 个元素结尾的最大子数组和
        // g[i] 表示以第 i 个元素结尾的最小子数组和
        int n = nums.size();
        vector<int> f(n + 1), g(n + 1); // 定义大小为 n+1 的 f 和 g 表

        // 2. 初始化
        int fmax = INT_MIN; // 记录全局最大子数组和
        int gmin = INT_MAX; // 记录全局最小子数组和
        int sum = 0; // 记录数组的总和

        // 3. 填表
        for (int i = 1; i <= n; i++) {
            int x = nums[i - 1]; // 当前元素

            // 计算以第 i 个元素结尾的最大子数组和
            f[i] = max(x, x + f[i - 1]);
            fmax = max(fmax, f[i]); // 更新全局最大子数组和

            // 计算以第 i 个元素结尾的最小子数组和
            g[i] = min(x, x + g[i - 1]);
            gmin = min(gmin, g[i]); // 更新全局最小子数组和

            sum += x; // 累加数组的总和
        }

        // 4. 返回结果
        // 如果数组所有元素为负数,则直接返回 fmax
        // 否则,返回 max(fmax, sum - gmin)
        return sum == gmin ? fmax : max(fmax, sum - gmin);
    }
};

代码思路

  1. 问题分析

    • 目标是找到环形数组中的最大子数组和。

    • 环形数组的特点是子数组可以跨越数组的末尾和开头。

    • 通过动态规划解决,分为两种情况:

      • 最大子数组不跨越数组边界(普通最大子数组和)。

      • 最大子数组跨越数组边界(总和减去最小子数组和)。

  2. 状态定义

    • f[i]:以第i个元素结尾的最大子数组和。

    • g[i]:以第i个元素结尾的最小子数组和。

  3. 状态转移方程

    • f[i] = max(nums[i-1], f[i-1] + nums[i-1])

    • g[i] = min(nums[i-1], g[i-1] + nums[i-1])

  4. 初始化

    • f[0]g[0]没有意义,可以忽略。

    • fmax记录全局最大子数组和,gmin记录全局最小子数组和。

  5. 结果

    • 如果数组所有元素为负数,则直接返回fmax

    • 否则,返回max(fmax, sum - gmin),其中sum是数组的总和。


代码逻辑详解

  1. 创建 DP 表

    • int n = nums.size();

      • 获取数组的长度n

    • vector<int> f(n + 1), g(n + 1);

      • 定义两个大小为n+1的数组fg,分别用于存储最大子数组和和最小子数组和。

  2. 初始化

    • int fmax = INT_MIN;

      • 初始化fmax为最小整数值,用于记录全局最大子数组和。

    • int gmin = INT_MAX;

      • 初始化gmin为最大整数值,用于记录全局最小子数组和。

    • int sum = 0;

      • 初始化sum为0,用于记录数组的总和。

  3. 填表

    • for (int i = 1; i <= n; i++)

      • 遍历数组的每个元素,i表示当前元素的位置(从1开始)。

    • int x = nums[i - 1];

      • 获取当前元素x

    • f[i] = max(x, x + f[i - 1]);

      • 计算以第i个元素结尾的最大子数组和:

        • x:单独作为子数组。

        • x + f[i-1]:加入前面的子数组。

      • 取两者的最大值。

    • fmax = max(fmax, f[i]);

      • 更新全局最大子数组和fmax

    • g[i] = min(x, x + g[i - 1]);

      • 计算以第i个元素结尾的最小子数组和:

        • x:单独作为子数组。

        • x + g[i-1]:加入前面的子数组。

      • 取两者的最小值。

    • gmin = min(gmin, g[i]);

      • 更新全局最小子数组和gmin

    • sum += x;

      • 累加数组的总和。

  4. 返回结果

    • return sum == gmin ? fmax : max(fmax, sum - gmin);

      • 如果数组所有元素为负数(sum == gmin),则直接返回fmax

      • 否则,返回max(fmax, sum - gmin),即最大子数组和或跨越边界的情况。


示例

假设输入数组为:nums = [5, -3, 5]

执行过程:

  1. 初始化:

    • f = [0, 0, 0, 0]

    • g = [0, 0, 0, 0]

    • fmax = INT_MIN

    • gmin = INT_MAX

    • sum = 0

  2. 填表:

    • i=1

      • x = 5

      • f[1] = max(5, 5 + 0) = 5fmax = max(INT_MIN, 5) = 5

      • g[1] = min(5, 5 + 0) = 5gmin = min(INT_MAX, 5) = 5

      • sum = 5

    • i=2

      • x = -3

      • f[2] = max(-3, -3 + 5) = 2fmax = max(5, 2) = 5

      • g[2] = min(-3, -3 + 5) = -3gmin = min(5, -3) = -3

      • sum = 2

    • i=3

      • x = 5

      • f[3] = max(5, 5 + 2) = 7fmax = max(5, 7) = 7

      • g[3] = min(5, 5 + -3) = 2gmin = min(-3, 2) = -3

      • sum = 7

  3. 返回结果:

    • sum == gminfalse,返回max(7, 7 - (-3)) = max(7, 10) = 10


总结

  • 问题本质:动态规划解决环形数组的最大子数组和问题。

  • 核心思想

    • 通过f[i]计算不跨越边界的最大子数组和。

    • 通过g[i]计算最小子数组和,用于处理跨越边界的情况。

  • 时间复杂度O(n),遍历数组一次。

  • 空间复杂度O(n),使用了两个大小为n+1的数组。可以通过滚动变量优化为O(1)

三、152. 乘积最大子数组 - 力扣(LeetCode) 

算法代码: 

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        // 1. 创建 dp 表
        // f[i] 表示以第 i 个元素结尾的子数组的最大乘积
        // g[i] 表示以第 i 个元素结尾的子数组的最小乘积
        int n = nums.size();
        vector<int> f(n + 1), g(n + 1); // 定义大小为 n+1 的 f 和 g 表

        // 2. 初始化
        f[0] = g[0] = 1; // 空数组的乘积为1
        int ret = INT_MIN; // 记录全局最大乘积

        // 3. 填表
        for (int i = 1; i <= n; i++) {
            int x = nums[i - 1]; // 当前元素
            int y = f[i - 1] * nums[i - 1]; // 当前元素与前面最大乘积的乘积
            int z = g[i - 1] * nums[i - 1]; // 当前元素与前面最小乘积的乘积

            // 计算以第 i 个元素结尾的最大乘积
            f[i] = max(x, max(y, z));

            // 计算以第 i 个元素结尾的最小乘积
            g[i] = min(x, min(y, z));

            // 更新全局最大乘积
            ret = max(ret, f[i]);
        }

        // 4. 返回结果
        return ret; // 返回全局最大乘积
    }
};

代码思路

  1. 问题分析

    • 目标是找到数组中连续子数组的最大乘积。

    • 由于乘积可能为正或负,需要同时记录以每个位置结尾的最大乘积和最小乘积。

  2. 状态定义

    • f[i]:以第i个元素结尾的子数组的最大乘积。

    • g[i]:以第i个元素结尾的子数组的最小乘积。

  3. 状态转移方程

    • 对于第i个元素,有三种选择:

      • 单独作为一个子数组:nums[i-1]

      • 加入前面的最大乘积子数组:f[i-1] * nums[i-1]

      • 加入前面的最小乘积子数组:g[i-1] * nums[i-1]

    • 取三者的最大值和最小值:

      • f[i] = max(nums[i-1], f[i-1] * nums[i-1], g[i-1] * nums[i-1])

      • g[i] = min(nums[i-1], f[i-1] * nums[i-1], g[i-1] * nums[i-1])

  4. 初始化

    • f[0]g[0]初始化为1,表示空数组的乘积为1(不影响后续计算)。

  5. 结果

    • 遍历f表,找到最大值。


代码逻辑详解

  1. 创建 DP 表

    • int n = nums.size();

      • 获取数组的长度n

    • vector<int> f(n + 1), g(n + 1);

      • 定义两个大小为n+1的数组fg,分别用于存储最大乘积和最小乘积。

  2. 初始化

    • f[0] = g[0] = 1;

      • 初始化f[0]g[0]为1,表示空数组的乘积为1(不影响后续计算)。

    • int ret = INT_MIN;

      • 初始化ret为最小整数值,用于记录全局最大乘积。

  3. 填表

    • for (int i = 1; i <= n; i++)

      • 遍历数组的每个元素,i表示当前元素的位置(从1开始)。

    • int x = nums[i - 1];

      • 获取当前元素x

    • int y = f[i - 1] * nums[i - 1];

      • 计算当前元素与前面最大乘积的乘积。

    • int z = g[i - 1] * nums[i - 1];

      • 计算当前元素与前面最小乘积的乘积。

    • f[i] = max(x, max(y, z));

      • 计算以第i个元素结尾的最大乘积:

        • x:单独作为子数组。

        • y:加入前面的最大乘积子数组。

        • z:加入前面的最小乘积子数组。

      • 取三者的最大值。

    • g[i] = min(x, min(y, z));

      • 计算以第i个元素结尾的最小乘积:

        • x:单独作为子数组。

        • y:加入前面的最大乘积子数组。

        • z:加入前面的最小乘积子数组。

      • 取三者的最小值。

    • ret = max(ret, f[i]);

      • 更新全局最大乘积ret

  4. 返回结果

    • return ret;

      • 返回全局最大乘积。


示例

假设输入数组为:nums = [2, 3, -2, 4]

执行过程:

  1. 初始化:

    • f = [1, 0, 0, 0, 0]

    • g = [1, 0, 0, 0, 0]

    • ret = INT_MIN

  2. 填表:

    • i=1

      • x = 2

      • y = 1 * 2 = 2

      • z = 1 * 2 = 2

      • f[1] = max(2, max(2, 2)) = 2

      • g[1] = min(2, min(2, 2)) = 2

      • ret = max(INT_MIN, 2) = 2

    • i=2

      • x = 3

      • y = 2 * 3 = 6

      • z = 2 * 3 = 6

      • f[2] = max(3, max(6, 6)) = 6

      • g[2] = min(3, min(6, 6)) = 3

      • ret = max(2, 6) = 6

    • i=3

      • x = -2

      • y = 6 * -2 = -12

      • z = 3 * -2 = -6

      • f[3] = max(-2, max(-12, -6)) = -2

      • g[3] = min(-2, min(-12, -6)) = -12

      • ret = max(6, -2) = 6

    • i=4

      • x = 4

      • y = -2 * 4 = -8

      • z = -12 * 4 = -48

      • f[4] = max(4, max(-8, -48)) = 4

      • g[4] = min(4, min(-8, -48)) = -48

      • ret = max(6, 4) = 6

  3. 返回结果:6


总结

  • 问题本质:动态规划解决最大子数组乘积问题。

  • 核心思想

    • 通过f[i]记录以第i个元素结尾的最大乘积。

    • 通过g[i]记录以第i个元素结尾的最小乘积,用于处理负数情况。

  • 时间复杂度O(n),遍历数组一次。

  • 空间复杂度O(n),使用了两个大小为n+1的数组。可以通过滚动变量优化为O(1)

四、1567. 乘积为正数的最长子数组长度 - 力扣(LeetCode) 

算法代码: 

class Solution {
public:
    int getMaxLen(vector<int>& nums) {
        // 1. 创建 dp 表
        // f[i] 表示以第 i 个元素结尾的乘积为正数的子数组的最大长度
        // g[i] 表示以第 i 个元素结尾的乘积为负数的子数组的最大长度
        int n = nums.size();
        vector<int> f(n + 1), g(n + 1); // 定义大小为 n+1 的 f 和 g 表

        // 2. 初始化
        int ret = 0; // 记录全局最大长度

        // 3. 填表
        for (int i = 1; i <= n; i++) {
            if (nums[i - 1] > 0) { // 当前元素为正数
                f[i] = f[i - 1] + 1; // 正数乘以正数仍为正数
                g[i] = g[i - 1] == 0 ? 0 : g[i - 1] + 1; // 正数乘以负数为负数
            } else if (nums[i - 1] < 0) { // 当前元素为负数
                f[i] = g[i - 1] == 0 ? 0 : g[i - 1] + 1; // 负数乘以负数为正数
                g[i] = f[i - 1] + 1; // 负数乘以正数为负数
            } else { // 当前元素为0
                f[i] = 0; // 乘积为0,无法形成正数的子数组
                g[i] = 0; // 乘积为0,无法形成负数的子数组
            }
            ret = max(ret, f[i]); // 更新全局最大长度
        }

        // 4. 返回结果
        return ret; // 返回全局最大长度
    }
};

代码思路

  1. 问题分析

    • 目标是找到数组中乘积为正数的连续子数组的最大长度。

    • 由于乘积的正负性取决于负数的个数,需要同时记录以每个位置结尾的乘积为正数和负数的子数组的最大长度。

  2. 状态定义

    • f[i]:以第i个元素结尾的乘积为正数的子数组的最大长度。

    • g[i]:以第i个元素结尾的乘积为负数的子数组的最大长度。

  3. 状态转移方程

    • 如果当前元素为正数:

      • f[i] = f[i-1] + 1(正数乘以正数仍为正数)。

      • g[i] = g[i-1] == 0 ? 0 : g[i-1] + 1(正数乘以负数为负数)。

    • 如果当前元素为负数:

      • f[i] = g[i-1] == 0 ? 0 : g[i-1] + 1(负数乘以负数为正数)。

      • g[i] = f[i-1] + 1(负数乘以正数为负数)。

    • 如果当前元素为0:

      • f[i] = 0g[i] = 0(乘积为0,无法形成正数或负数的子数组)。

  4. 初始化

    • f[0]g[0]初始化为0,表示空数组的长度为0。

  5. 结果

    • 遍历f表,找到最大值。


代码逻辑详解

  1. 创建 DP 表

    • int n = nums.size();

      • 获取数组的长度n

    • vector<int> f(n + 1), g(n + 1);

      • 定义两个大小为n+1的数组fg,分别用于存储乘积为正数和负数的子数组的最大长度。

  2. 初始化

    • int ret = 0;

      • 初始化ret为0,用于记录全局最大长度。

  3. 填表

    • for (int i = 1; i <= n; i++)

      • 遍历数组的每个元素,i表示当前元素的位置(从1开始)。

    • if (nums[i - 1] > 0)

      • 如果当前元素为正数:

        • f[i] = f[i - 1] + 1;

          • 正数乘以正数仍为正数,长度加1。

        • g[i] = g[i - 1] == 0 ? 0 : g[i - 1] + 1;

          • 正数乘以负数为负数,如果前面没有负数的子数组,则长度为0。

    • else if (nums[i - 1] < 0)

      • 如果当前元素为负数:

        • f[i] = g[i - 1] == 0 ? 0 : g[i - 1] + 1;

          • 负数乘以负数为正数,如果前面没有负数的子数组,则长度为0。

        • g[i] = f[i - 1] + 1;

          • 负数乘以正数为负数,长度加1。

    • else

      • 如果当前元素为0:

        • f[i] = 0;

          • 乘积为0,无法形成正数的子数组。

        • g[i] = 0;

          • 乘积为0,无法形成负数的子数组。

    • ret = max(ret, f[i]);

      • 更新全局最大长度ret

  4. 返回结果

    • return ret;

      • 返回全局最大长度。


示例

假设输入数组为:nums = [1, -2, -3, 4]

执行过程:

  1. 初始化:

    • f = [0, 0, 0, 0, 0]

    • g = [0, 0, 0, 0, 0]

    • ret = 0

  2. 填表:

    • i=1

      • nums[0] = 1(正数)

      • f[1] = f[0] + 1 = 1

      • g[1] = g[0] == 0 ? 0 : g[0] + 1 = 0

      • ret = max(0, 1) = 1

    • i=2

      • nums[1] = -2(负数)

      • f[2] = g[1] == 0 ? 0 : g[1] + 1 = 0

      • g[2] = f[1] + 1 = 2

      • ret = max(1, 0) = 1

    • i=3

      • nums[2] = -3(负数)

      • f[3] = g[2] == 0 ? 0 : g[2] + 1 = 3

      • g[3] = f[2] + 1 = 1

      • ret = max(1, 3) = 3

    • i=4

      • nums[3] = 4(正数)

      • f[4] = f[3] + 1 = 4

      • g[4] = g[3] == 0 ? 0 : g[3] + 1 = 0

      • ret = max(3, 4) = 4

  3. 返回结果:4


总结

  • 问题本质:动态规划解决乘积为正数的子数组的最大长度问题。

  • 核心思想

    • 通过f[i]记录以第i个元素结尾的乘积为正数的子数组的最大长度。

    • 通过g[i]记录以第i个元素结尾的乘积为负数的子数组的最大长度。

  • 时间复杂度O(n),遍历数组一次。

  • 空间复杂度O(n),使用了两个大小为n+1的数组。可以通过滚动变量优化为O(1)

五、413. 等差数列划分 - 力扣(LeetCode) 

算法代码:

class Solution {
public:
    int numberOfArithmeticSlices(vector<int>& nums) {
        // 1. 创建 dp 表
        // dp[i] 表示以第 i 个元素结尾的等差数列的个数
        int n = nums.size();
        vector<int> dp(n); // 定义大小为 n 的 dp 表

        // 2. 初始化
        int sum = 0; // 用于累加所有等差数列的个数

        // 3. 填表
        for (int i = 2; i < n; i++) {
            // 检查当前元素是否可以加入前面的等差数列
            if (nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2]) {
                // 如果可以加入,则等差数列的个数加1
                dp[i] = dp[i - 1] + 1;
            } else {
                // 如果不能加入,则等差数列的个数为0
                dp[i] = 0;
            }
            // 累加当前等差数列的个数
            sum += dp[i];
        }

        // 4. 返回结果
        return sum; // 返回等差数列的总数
    }
};

代码思路

  1. 问题分析

    • 目标是找到数组中等差数列的个数。

    • 等差数列的定义是:至少包含3个元素,且相邻元素的差值相等。

  2. 状态定义

    • dp[i]:以第i个元素结尾的等差数列的个数。

  3. 状态转移方程

    • 如果nums[i] - nums[i-1] == nums[i-1] - nums[i-2],则当前元素可以加入前面的等差数列,形成新的等差数列。

      • dp[i] = dp[i-1] + 1

    • 否则,当前元素不能加入前面的等差数列,dp[i] = 0

  4. 初始化

    • dp[0]dp[1]初始化为0,因为至少需要3个元素才能形成等差数列。

  5. 结果

    • 遍历dp表,累加所有dp[i]的值,得到等差数列的总数。


代码逻辑详解

  1. 创建 DP 表

    • int n = nums.size();

      • 获取数组的长度n

    • vector<int> dp(n);

      • 定义一个大小为n的数组dp,用于存储以每个位置结尾的等差数列的个数。

  2. 初始化

    • int sum = 0;

      • 初始化sum为0,用于累加所有等差数列的个数。

  3. 填表

    • for (int i = 2; i < n; i++)

      • 遍历数组的每个元素,i表示当前元素的位置(从2开始,因为至少需要3个元素才能形成等差数列)。

    • if (nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2])

      • 检查当前元素是否可以加入前面的等差数列:

        • 如果nums[i] - nums[i-1] == nums[i-1] - nums[i-2],则当前元素可以加入前面的等差数列。

    • dp[i] = dp[i - 1] + 1;

      • 如果可以加入,则等差数列的个数加1。

    • else dp[i] = 0;

      • 如果不能加入,则等差数列的个数为0。

    • sum += dp[i];

      • 累加当前等差数列的个数。

  4. 返回结果

    • return sum;

      • 返回等差数列的总数。


示例

假设输入数组为:nums = [1, 2, 3, 4]

执行过程:

  1. 初始化:

    • dp = [0, 0, 0, 0]

    • sum = 0

  2. 填表:

    • i=2

      • nums[2] - nums[1] = 1nums[1] - nums[0] = 1

      • dp[2] = dp[1] + 1 = 1

      • sum = 0 + 1 = 1

    • i=3

      • nums[3] - nums[2] = 1nums[2] - nums[1] = 1

      • dp[3] = dp[2] + 1 = 2

      • sum = 1 + 2 = 3

  3. 返回结果:3


总结

  • 问题本质:动态规划解决等差数列的个数问题。

  • 核心思想

    • 通过dp[i]记录以第i个元素结尾的等差数列的个数。

    • 如果当前元素可以加入前面的等差数列,则dp[i] = dp[i-1] + 1,否则dp[i] = 0

  • 时间复杂度O(n),遍历数组一次。

  • 空间复杂度O(n),使用了一个大小为n的数组。可以通过滚动变量优化为O(1)

六、978. 最长湍流子数组 - 力扣(LeetCode)

算法代码: 

class Solution {
public:
    int maxTurbulenceSize(vector<int>& arr) {
        // 1. 创建 dp 表
        // f[i] 表示以第 i 个元素结尾且最后两个元素是上升趋势的湍流子数组的最大长度
        // g[i] 表示以第 i 个元素结尾且最后两个元素是下降趋势的湍流子数组的最大长度
        int n = arr.size();
        vector<int> f(n, 1), g(n, 1); // 定义大小为 n 的 f 和 g 表,初始化为1

        // 2. 初始化
        int ret = 1; // 记录全局最大长度,初始化为1(至少有一个元素)

        // 3. 填表
        for (int i = 1; i < n; i++) {
            if (arr[i - 1] < arr[i]) { // 当前元素比前一个元素大
                f[i] = g[i - 1] + 1; // 上升趋势,更新 f[i]
            } else if (arr[i - 1] > arr[i]) { // 当前元素比前一个元素小
                g[i] = f[i - 1] + 1; // 下降趋势,更新 g[i]
            }
            // 如果 arr[i-1] == arr[i],则 f[i] 和 g[i] 保持为1
            ret = max(ret, max(f[i], g[i])); // 更新全局最大长度
        }

        // 4. 返回结果
        return ret; // 返回全局最大长度
    }
};

代码思路

  1. 问题分析

    • 目标是找到数组中最长的湍流子数组的长度。

    • 湍流子数组的定义是:相邻元素的差值符号交替变化(即大小关系交替变化)。

  2. 状态定义

    • f[i]:以第i个元素结尾且最后两个元素是上升趋势(arr[i-1] < arr[i])的湍流子数组的最大长度。

    • g[i]:以第i个元素结尾且最后两个元素是下降趋势(arr[i-1] > arr[i])的湍流子数组的最大长度。

  3. 状态转移方程

    • 如果arr[i-1] < arr[i],则当前元素可以加入前面的下降趋势子数组,形成新的上升趋势子数组:

      • f[i] = g[i-1] + 1

    • 如果arr[i-1] > arr[i],则当前元素可以加入前面的上升趋势子数组,形成新的下降趋势子数组:

      • g[i] = f[i-1] + 1

    • 如果arr[i-1] == arr[i],则当前元素无法加入前面的湍流子数组,f[i] = 1g[i] = 1

  4. 初始化

    • f[i]g[i]初始化为1,表示每个元素本身可以作为一个长度为1的湍流子数组。

  5. 结果

    • 遍历fg表,找到最大值。


代码逻辑详解

  1. 创建 DP 表

    • int n = arr.size();

      • 获取数组的长度n

    • vector<int> f(n, 1), g(n, 1);

      • 定义两个大小为n的数组fg,初始化为1,表示每个元素本身可以作为一个长度为1的湍流子数组。

  2. 初始化

    • int ret = 1;

      • 初始化ret为1,表示至少有一个元素。

  3. 填表

    • for (int i = 1; i < n; i++)

      • 遍历数组的每个元素,i表示当前元素的位置(从1开始)。

    • if (arr[i - 1] < arr[i])

      • 如果当前元素比前一个元素大:

        • f[i] = g[i - 1] + 1;

          • 当前元素可以加入前面的下降趋势子数组,形成新的上升趋势子数组。

    • else if (arr[i - 1] > arr[i])

      • 如果当前元素比前一个元素小:

        • g[i] = f[i - 1] + 1;

          • 当前元素可以加入前面的上升趋势子数组,形成新的下降趋势子数组。

    • ret = max(ret, max(f[i], g[i]));

      • 更新全局最大长度ret

  4. 返回结果

    • return ret;

      • 返回全局最大长度。


示例

假设输入数组为:arr = [9, 4, 2, 10, 7, 8, 8, 1, 9]

执行过程:

  1. 初始化:

    • f = [1, 1, 1, 1, 1, 1, 1, 1, 1]

    • g = [1, 1, 1, 1, 1, 1, 1, 1, 1]

    • ret = 1

  2. 填表:

    • i=1

      • arr[0] = 9arr[1] = 4(下降)

      • g[1] = f[0] + 1 = 2

      • ret = max(1, 2) = 2

    • i=2

      • arr[1] = 4arr[2] = 2(下降)

      • g[2] = f[1] + 1 = 2

      • ret = max(2, 2) = 2

    • i=3

      • arr[2] = 2arr[3] = 10(上升)

      • f[3] = g[2] + 1 = 3

      • ret = max(2, 3) = 3

    • i=4

      • arr[3] = 10arr[4] = 7(下降)

      • g[4] = f[3] + 1 = 4

      • ret = max(3, 4) = 4

    • i=5

      • arr[4] = 7arr[5] = 8(上升)

      • f[5] = g[4] + 1 = 5

      • ret = max(4, 5) = 5

    • i=6

      • arr[5] = 8arr[6] = 8(相等)

      • f[6] = 1g[6] = 1

      • ret = max(5, 1) = 5

    • i=7

      • arr[6] = 8arr[7] = 1(下降)

      • g[7] = f[6] + 1 = 2

      • ret = max(5, 2) = 5

    • i=8

      • arr[7] = 1arr[8] = 9(上升)

      • f[8] = g[7] + 1 = 3

      • ret = max(5, 3) = 5

  3. 返回结果:5


总结

  • 问题本质:动态规划解决最长湍流子数组问题。

  • 核心思想

    • 通过f[i]记录以第i个元素结尾且最后两个元素是上升趋势的湍流子数组的最大长度。

    • 通过g[i]记录以第i个元素结尾且最后两个元素是下降趋势的湍流子数组的最大长度。

  • 时间复杂度O(n),遍历数组一次。

  • 空间复杂度O(n),使用了两个大小为n的数组。可以通过滚动变量优化为O(1)

 

七、139. 单词拆分 - 力扣(LeetCode)

算法代码: 

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        // 优化⼀:将字典⾥的单词存在哈希表⾥
        unordered_set<string> hash;
        for (auto& s : wordDict)
            hash.insert(s); // 将字典中的单词插入哈希表

        int n = s.size(); // 获取字符串的长度
        vector<bool> dp(n + 1); // 定义大小为 n+1 的 dp 表
        dp[0] = true; // 初始化 dp[0] 为 true,表示空字符串可以被拼接

        s = ' ' + s; // 在字符串前添加一个空格,使下标从 1 开始

        // 填 dp[i]
        for (int i = 1; i <= n; i++) {
            // 枚举最后一个单词的起始位置 j
            for (int j = i; j >= 1; j--) {
                // 如果 dp[j-1] 为 true,且 s[j..i] 在字典中
                if (dp[j - 1] && hash.count(s.substr(j, i - j + 1))) {
                    dp[i] = true; // 更新 dp[i] 为 true
                    break; // 优化⼆:找到满足条件的 j 后立即跳出循环
                }
            }
        }

        // 返回 dp[n],表示整个字符串是否可以被拼接
        return dp[n];
    }
};

代码思路

  1. 问题分析

    • 目标是判断字符串s是否可以由字典wordDict中的单词拼接而成。

    • 使用动态规划解决,定义dp[i]表示字符串s的前i个字符是否可以被字典中的单词拼接。

  2. 状态定义

    • dp[i]:字符串s的前i个字符是否可以被字典中的单词拼接。

  3. 状态转移方程

    • 对于每个位置i,枚举最后一个单词的起始位置j(从i1):

      • 如果dp[j-1]true,且s[j..i]在字典中,则dp[i] = true

    • 如果找不到满足条件的j,则dp[i] = false

  4. 初始化

    • dp[0] = true,表示空字符串可以被拼接(即不选任何单词)。

  5. 优化

    • 将字典中的单词存储在哈希表中,方便快速查找。

    • 在找到满足条件的j后,立即跳出内层循环(优化二)。


代码逻辑详解

  1. 优化一:哈希表存储字典

    • unordered_set<string> hash;

      • 定义一个哈希表hash,用于存储字典中的单词。

    • for (auto& s : wordDict) hash.insert(s);

      • 将字典中的单词插入哈希表,方便快速查找。

  2. 初始化 DP 表

    • int n = s.size();

      • 获取字符串s的长度n

    • vector<bool> dp(n + 1);

      • 定义一个大小为n+1的布尔数组dp,用于存储状态。

    • dp[0] = true;

      • 初始化dp[0]true,表示空字符串可以被拼接。

  3. 调整字符串下标

    • s = ' ' + s;

      • 在字符串s前添加一个空格,使字符串的下标从1开始,方便后续处理。

  4. 填表

    • for (int i = 1; i <= n; i++)

      • 遍历字符串的每个位置,i表示当前字符的位置(从1开始)。

    • for (int j = i; j >= 1; j--)

      • 枚举最后一个单词的起始位置j(从i1)。

    • if (dp[j - 1] && hash.count(s.substr(j, i - j + 1)))

      • 如果dp[j-1]true,且s[j..i]在字典中:

        • dp[j-1]表示前j-1个字符可以被拼接。

        • s.substr(j, i - j + 1)表示从ji的子串。

        • hash.count(...)检查子串是否在字典中。

    • dp[i] = true;

      • 如果满足条件,则dp[i]true

    • break;

      • 找到满足条件的j后,立即跳出内层循环(优化二)。

  5. 返回结果

    • return dp[n];

      • 返回dp[n],表示整个字符串是否可以被拼接。


示例

假设输入:

  • s = "leetcode"

  • wordDict = ["leet", "code"]

执行过程:

  1. 初始化:

    • hash = {"leet", "code"}

    • dp = [true, false, false, false, false, false, false, false, false]

    • s = " leetcode"

  2. 填表:

    • i=1

      • j=1s[1..1] = "l"不在字典中。

      • dp[1] = false

    • i=2

      • j=2s[2..2] = "e"不在字典中。

      • j=1s[1..2] = "le"不在字典中。

      • dp[2] = false

    • i=3

      • j=3s[3..3] = "e"不在字典中。

      • j=2s[2..3] = "ee"不在字典中。

      • j=1s[1..3] = "lee"不在字典中。

      • dp[3] = false

    • i=4

      • j=4s[4..4] = "t"不在字典中。

      • j=3s[3..4] = "et"不在字典中。

      • j=2s[2..4] = "eet"不在字典中。

      • j=1s[1..4] = "leet"在字典中。

      • dp[4] = true

    • i=5

      • j=5s[5..5] = "c"不在字典中。

      • j=4s[4..5] = "tc"不在字典中。

      • j=3s[3..5] = "etc"不在字典中。

      • j=2s[2..5] = "eetc"不在字典中。

      • j=1s[1..5] = "leetc"不在字典中。

      • dp[5] = false

    • i=6

      • j=6s[6..6] = "o"不在字典中。

      • j=5s[5..6] = "co"不在字典中。

      • j=4s[4..6] = "tco"不在字典中。

      • j=3s[3..6] = "etco"不在字典中。

      • j=2s[2..6] = "eetco"不在字典中。

      • j=1s[1..6] = "leetco"不在字典中。

      • dp[6] = false

    • i=7

      • j=7s[7..7] = "d"不在字典中。

      • j=6s[6..7] = "od"不在字典中。

      • j=5s[5..7] = "cod"不在字典中。

      • j=4s[4..7] = "tcod"不在字典中。

      • j=3s[3..7] = "etcod"不在字典中。

      • j=2s[2..7] = "eetcod"不在字典中。

      • j=1s[1..7] = "leetcod"不在字典中。

      • dp[7] = false

    • i=8

      • j=8s[8..8] = "e"不在字典中。

      • j=7s[7..8] = "de"不在字典中。

      • j=6s[6..8] = "ode"不在字典中。

      • j=5s[5..8] = "code"在字典中。

      • dp[8] = true

  3. 返回结果:true


总结

  • 问题本质:动态规划解决字符串是否可以由字典中的单词拼接而成。

  • 核心思想

    • 通过dp[i]记录前i个字符是否可以被拼接。

    • 枚举最后一个单词的起始位置j,检查dp[j-1]s[j..i]是否满足条件。

  • 时间复杂度O(n^2),外层循环n次,内层循环最多n次。

  • 空间复杂度O(n),使用了一个大小为n+1dp表和一个哈希表。

八、467. 环绕字符串中唯一的子字符串 - 力扣(LeetCode) 

算法代码: 

class Solution {
public:
    int findSubstringInWraproundString(string s) {
        int n = s.size(); // 获取字符串的长度

        // 1. 利用 dp 求出每个位置结尾的最长连续子数组的长度
        vector<int> dp(n, 1); // 定义大小为 n 的 dp 表,初始化为1
        for (int i = 1; i < n; i++) {
            // 检查当前字符是否是前一个字符的下一个字母
            if (s[i] - 1 == s[i - 1] || (s[i - 1] == 'z' && s[i] == 'a')) {
                dp[i] = dp[i - 1] + 1; // 如果是,则长度加1
            }
        }

        // 2. 计算每一个字符结尾的最长连续子数组的长度
        int hash[26] = {0}; // 定义哈希表,记录以每个字母结尾的最长环绕子串的长度
        for (int i = 0; i < n; i++) {
            // 更新以当前字符结尾的最长环绕子串的长度
            hash[s[i] - 'a'] = max(hash[s[i] - 'a'], dp[i]);
        }

        // 3. 将结果累加起来
        int sum = 0; // 定义累加结果
        for (auto x : hash) {
            sum += x; // 累加哈希表中的值
        }

        return sum; // 返回最终结果
    }
};

代码思路

  1. 问题分析

    • 目标是找到字符串s中所有唯一的环绕子串的数量。

    • 环绕子串的定义是:子串中的字符是连续的字母(例如"abc""zab")。

  2. 状态定义

    • dp[i]:以第i个字符结尾的最长连续环绕子串的长度。

  3. 状态转移方程

    • 如果s[i]s[i-1]的下一个字母(例如'a''z'的下一个字母),则dp[i] = dp[i-1] + 1

    • 否则,dp[i] = 1

  4. 去重

    • 使用哈希表hash记录以每个字母结尾的最长环绕子串的长度。

    • 最终结果是哈希表中所有值的累加。


代码逻辑详解

  1. DP 表定义

    • int n = s.size();

      • 获取字符串s的长度n

    • vector<int> dp(n, 1);

      • 定义一个大小为n的数组dp,初始化为1,表示每个字符本身可以作为一个长度为1的环绕子串。

  2. 填 DP 表

    • for (int i = 1; i < n; i++)

      • 遍历字符串的每个字符,i表示当前字符的位置(从1开始)。

    • if (s[i] - 1 == s[i - 1] || (s[i - 1] == 'z' && s[i] == 'a'))

      • 检查当前字符是否是前一个字符的下一个字母:

        • s[i] - 1 == s[i - 1]:普通情况(例如'b''a'的下一个字母)。

        • s[i - 1] == 'z' && s[i] == 'a':特殊情况('a''z'的下一个字母)。

    • dp[i] = dp[i - 1] + 1;

      • 如果满足条件,则当前字符可以加入前面的环绕子串,长度加1。

  3. 哈希表记录

    • int hash[26] = {0};

      • 定义一个大小为26的数组hash,用于记录以每个字母结尾的最长环绕子串的长度。

    • for (int i = 0; i < n; i++)

      • 遍历字符串的每个字符。

    • hash[s[i] - 'a'] = max(hash[s[i] - 'a'], dp[i]);

      • 更新以当前字符结尾的最长环绕子串的长度。

  4. 累加结果

    • int sum = 0;

      • 定义一个变量sum,用于累加哈希表中的值。

    • for (auto x : hash)

      • 遍历哈希表。

    • sum += x;

      • 累加以每个字母结尾的最长环绕子串的长度。

  5. 返回结果

    • return sum;

      • 返回累加结果。


示例

假设输入字符串为:s = "zab"

执行过程:

  1. 初始化:

    • dp = [1, 1, 1]

    • hash = [0, 0, 0, ..., 0](26个0)

  2. 填 DP 表:

    • i=1

      • s[1] = 'a's[0] = 'z',满足'a''z'的下一个字母。

      • dp[1] = dp[0] + 1 = 2

    • i=2

      • s[2] = 'b's[1] = 'a',满足'b''a'的下一个字母。

      • dp[2] = dp[1] + 1 = 3

  3. 哈希表记录:

    • i=0

      • s[0] = 'z'dp[0] = 1

      • hash['z' - 'a'] = max(0, 1) = 1

    • i=1

      • s[1] = 'a'dp[1] = 2

      • hash['a' - 'a'] = max(0, 2) = 2

    • i=2

      • s[2] = 'b'dp[2] = 3

      • hash['b' - 'a'] = max(0, 3) = 3

  4. 累加结果:

    • sum = 1 + 2 + 3 = 6

  5. 返回结果:6


总结

  • 问题本质:动态规划解决环绕子串的计数问题。

  • 核心思想

    • 通过dp[i]记录以第i个字符结尾的最长环绕子串的长度。

    • 使用哈希表去重,记录以每个字母结尾的最长环绕子串的长度。

  • 时间复杂度O(n),遍历字符串两次。

  • 空间复杂度O(n),使用了一个大小为ndp表和一个大小为26的哈希表。

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

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

相关文章

Windows编程----进程的当前目录

进程的当前目录 Windows Api中有大量的函数在调用的时候&#xff0c;需要传递路径。比如创建文件&#xff0c;创建目录&#xff0c;删除目录&#xff0c;删除文件等等。拿创建文件的CreateFile函数做比喻&#xff0c;如果我们要创建的文件路径不是全路径&#xff0c;那么wind…

AVL树的介绍及实现

文章目录 &#xff08;一&#xff09;AVL的概念&#xff08;二&#xff09;AVL树的实现1.AVL树的结构2.AVL树的插入3.AVL树的查找 &#xff08;三&#xff09;检查一棵树是否是AVL树 &#xff08;一&#xff09;AVL的概念 AVL树是一棵高度平衡的二叉搜索树&#xff0c;通过控制…

快速生成viso流程图图片形式

我们在写详细设计文档的过程中总会不可避免的涉及到时序图或者流程图的绘制&#xff0c;viso这个软件大部分技术人员都会使用&#xff0c;但是想要画的好看&#xff0c;画的科学还是比较难的&#xff0c;现在我总结一套比较好的方法可以生成好看科学的viso图(图片格式)。主要思…

【极光 Orbit•STC8A-8H】03. 小刀初试:点亮你的LED灯

【极光 Orbit•STC8H】03. 小刀初试&#xff1a;点亮你的 LED 灯 七律 点灯初探 单片方寸藏乾坤&#xff0c;LED明灭见真章。 端口配置定方向&#xff0c;寄存器值细推敲。 高低电平随心控&#xff0c;循环闪烁展锋芒。 嵌入式门初开启&#xff0c;从此代码手中扬。 摘要 …

OSPF报文分析

OSPF报文分析 组播地址 224.0.0.0&#xff5e;224.0.0.255为预留的组播地址&#xff08;永久组地址&#xff09;&#xff0c;地址224.0.0.0保留不做分配&#xff0c;其它地址供路由协议使用&#xff1b; 224.0.1.0&#xff5e;238.255.255.255为用户可用的组播地址&#xff08;…

MySql性能(9)- mysql的order by的工作原理

全字段排序rowid排序全字段排序和rowid排序 3.1 联合索引优化 3.2 覆盖索引优化优先队列算法优化建议 5.1 修改系统参数 5.2 优化sql 1. 全字段排序 CREATE TABLE t ( id int(11) NOT NULL,city varchar(16) NOT NULL, name varchar(16) NOT NULL, age int(11) NOT NULL,addr v…

爬虫案例七Python协程爬取视频

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Python协程爬取视频 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 爬虫案例七协程爬取视频 提示&#xff1a;以下是本篇文章正文…

智慧城市智慧社区项目建设方案

一、项目背景 在全球化进程加速的今天&#xff0c;城市化问题日益凸显&#xff0c;传统的城市管理模式已难以满足现代社会对高效、智能化管理的需求。智慧城市和智慧社区的概念应运而生&#xff0c;其核心目标是通过信息技术手段&#xff0c;提升城市资源的利用效率&#xff0…

RabbitMQ高级特性--消息确认机制

目录 一、消息确认 1.消息确认机制 2.手动确认方法 二、代码示例 1. AcknowledgeMode.NONE 1.1 配置文件 1.2 生产者 1.3 消费者 1.4 运行程序 2.AcknowledgeMode.AUTO 3.AcknowledgeMode.MANUAL 一、消息确认 1.消息确认机制 生产者发送消息之后&#xff0c;到达消…

Java EE 进阶:Spring IoCDI

IOC的简单介绍 什么是Spring&#xff1f;Spring是一个开源的框架&#xff0c;让我们的开发更加的简单&#xff0c;我们可以用一句更加具体的话来概括Spring&#xff0c;就是Spring是一个包含众多工具方法的IOC容器。 简单介绍一下IOC&#xff0c;我们之前说过通过ReqestContr…

Java数据结构第二十期:解构排序算法的艺术与科学(二)

专栏&#xff1a;Java数据结构秘籍 个人主页&#xff1a;手握风云 目录 一、常见排序算法的实现 1.1. 直接选择排序 1.2. 堆排序 1.3. 冒泡排序 1.4. 快速排序 一、常见排序算法的实现 1.1. 直接选择排序 每⼀次从待排序的数据元素中选出最小的⼀个元素&#xff0c;存放在…

【算法day5】最长回文子串——马拉车算法

最长回文子串 给你一个字符串 s&#xff0c;找到 s 中最长的 回文 子串。 https://leetcode.cn/problems/longest-palindromic-substring/description/ 算法思路&#xff1a; class Solution { public:string longestPalindrome(string s) {int s_len s.size();string tmp …

《如何排查Linux系统平均负载过高》

【系统平均负载导读】何为系统平均负载&#xff1f;假设一台云服务主机&#xff0c;突然之间响应用户请求的时间变长了&#xff0c;那么这个时候应该如何去排查&#xff1f;带着这个问题&#xff0c;我们对“平均负载”展开深入的探讨和研究。 何为Linux系统的平均负载&#xf…

基于DeepSeek实现PDF嵌入SVG图片无损放大

1. PDF中效果图 2. 询问Deepseek进行代码书写&#xff0c;不断优化后结果 /*** SVG工具类&#xff0c;用于生成价格趋势的SVG图表*/ public class SvgUtils {// SVG画布尺寸private static final int WIDTH 800;private static final int HEIGHT 500;private static final i…

整型的不同类型和溢出

一、整数的不同类型 不同编程语言中的整数类型主要通过以下两个维度区分&#xff1a; 1. 存储大小 字节数&#xff1a;决定整数能表示的范围&#xff08;如 1字节8位&#xff09;。 常见类型&#xff1a; byte&#xff08;1字节&#xff09;、short&#xff08;2字节&#x…

使用express创建服务器保存数据到mysql

创建数据库和表结构 CREATE DATABASE collect;USE collect;CREATE TABLE info (id int(11) NOT NULL AUTO_INCREMENT,create_date bigint(20) DEFAULT NULL COMMENT 时间,type varchar(20) DEFAULT NULL COMMENT 数据分类,text_value text COMMENT 内容,PRIMARY KEY (id) ) EN…

小程序 wxml 语法 —— 41列表渲染 - 进阶用法

这一节讲解列表渲染的两个进阶用法&#xff1a; 如果需要对默认的变量名和下标进行修改&#xff0c;可以使用 wx:for-item 和 wx:for-item&#xff1a; 使用 wx:for-item 可以指定数组当前元素的变量名使用 wx:for-index 可以指定数组当前下标的变量名 将 wx:for 用在 标签上&…

python语言总结(持续更新)

本文主要是总结各函数&#xff0c;简单的函数不会给予示例&#xff0c;如果在平日遇到一些新类型将会添加 基础知识 输入与输出 print([要输出的内容])输出函数 input([提示内容]如果输入提示内容会在交互界面显示&#xff0c;用以提示用户)输入函数 注释 # 单行注释符&…

FPGA学习篇——Verilog学习5(reg,wire区分及模块例化)

1 何时用reg&#xff0c;何时用wire&#xff1f; 这个我找了一些网上的各种资料&#xff0c;大概说一下自己的理解&#xff0c;可能还不太到位... wire相当于一根线&#xff0c;是实时传输的那种&#xff0c;而reg是一个寄存器&#xff0c;是可以存储数据的&#xff0c;需要立…

Redis 数据持久化之AOF

AOF&#xff08;Append Only File&#xff09; 以日志的形式来记录每个写操作&#xff0c;将Redis执行过的所有写指令记录下来&#xff08;读操作不记录&#xff09;&#xff0c;只许追加文件但不可以改写文件&#xff0c;redis启动之初会读取该文件重新构建数据&#xff0c;换…