当前位置: 首页 > news >正文

每日算法-250323

💣 拆炸弹:滑动窗口的巧妙应用

拆炸弹

题目描述:

给定一个循环数组 code 和一个整数 k,你需要根据 k 的值来修改 code 数组:

  • 如果 k > 0,将 code[i] 替换为 k 个数 的和。
  • 如果 k < 0,将 code[i] 替换为 k 个数 的和。
  • 如果 k = 0,将 code[i] 替换为 0。

题目图片:

拆炸弹题目

思路分析:

这道题的核心在于如何高效地计算 code[i] 对应的替换值。直接暴力求解会导致大量重复计算。观察到,我们可以利用 滑动窗口 的思想,维护一个大小为 k 的窗口,计算窗口内元素的和,从而避免重复计算。

解题过程:

  1. 特殊情况处理:k = 0 时,直接返回一个全为 0 的新数组。

  2. 处理负数 k 为了统一处理逻辑,当 k 为负数时,我们将 code 数组 倒置,并将 k 取绝对值。这样,负数 k 的问题就转化为了正数 k 的问题。

  3. 初始化滑动窗口: 计算初始窗口的和 sum,即 code 数组前 a 个元素的和(ak 的绝对值)。

  4. 滑动窗口:

    • 维护一个左边界 left 和右边界 right,初始时 left = 1right = a + 1
    • 每次迭代,将窗口向右移动一位:
      • 由于 code 是循环数组,当 right 超出数组范围时,将其置为 0。
      • 更新窗口的和 sumsum = sum - code[left] + code[right]
      • 更新 ret[left] 的值为 sum
      • leftright 分别加 1。
  5. 处理负数 k 的结果: 如果 k 为负数,在返回结果前,需要将 ret 数组 倒置

复杂度分析:

  • 时间复杂度:O(n),其中 ncode 数组的长度。
  • 空间复杂度:O(n),用于存储结果数组 ret

代码实现(Java):

class Solution {
    public int[] decrypt(int[] code, int k) {
        int n = code.length;
        int[] ret = new int[n];
        if (k == 0) {
            return ret;
        }
        int a = k;
        if (a < 0) {
            a = -a;
            reverse(code);
        }
        for (int i = 1; i <= a; i++) {
            ret[0] += code[i];
        }
        int sum = ret[0];
        for (int left = 1, right = a + 1; left < n; right++) {
            if (right > n - 1) {
                right = 0;
            }
            sum = sum - code[left] + code[right];
            ret[left] = sum;
            left++;
        }
        if (k < 0) {
            reverse(ret);
        }
        return ret;
    }

    private void reverse(int[] code) {
        int l = 0, r = code.length - 1;
        while (l < r) {
            int tmp = code[l];
            code[l] = code[r];
            code[r] = tmp;
            l++;
            r--;
        }
    }
}

🔤 无重复字符的最长子串:滑动窗口的经典应用

3. 无重复字符的最长子串

题目描述:

给定一个字符串 s,请你找出其中不含有重复字符的 最长子串 的长度。

题目图片:

无重复字符的最长子串题目

思路分析:

这道题同样可以使用 滑动窗口 来解决。我们维护一个窗口,窗口内的字符都是不重复的。当遇到重复字符时,我们需要移动窗口的左边界,直到窗口内没有重复字符为止。

解题过程:

  1. 使用哈希表记录字符出现次数: 由于字符串 s 由英文字母、数字、符号和空格组成,我们可以使用一个长度为 128 的数组 map 来记录每个字符出现的次数。

  2. 滑动窗口:

    • 维护一个左边界 left 和右边界 right,初始时都为 0。
    • 每次迭代,将右边界 right 向右移动一位:
      • s[right] 对应的字符在 map 中的值加 1。
      • 如果 map[s[right]] > 1,说明出现了重复字符,需要移动左边界:
        • 不断将 s[left] 对应的字符在 map 中的值减 1,并将 left 加 1,直到 map[s[right]] <= 1 为止。
      • 更新最长无重复子串的长度 retret = Math.max(ret, right - left + 1)

复杂度分析:

  • 时间复杂度:O(n),其中 n 是字符串 s 的长度。
  • 空间复杂度:O(1),map 数组的长度是固定的。

代码实现(Java):

class Solution {
    public int lengthOfLongestSubstring(String ss) {
        char[] s = ss.toCharArray();
        int ret = 0, n = s.length;
        int[] map = new int[128];
        for (int left = 0, right = 0; right < n; right++) {
            char in = s[right];
            map[in]++;
            while (map[in] > 1) {
                map[s[left]]--;
                left++;
            }
            // 窗口合法,更新结果
            ret = Math.max(ret, right - left + 1);
        }
        return ret;
    }
}

➕ 最大子数组和:Kadane 算法的魅力

53. 最大子数组和

题目描述:

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

题目图片:

最大子数组和题目

思路分析:

这道题可以使用 Kadane 算法 来解决。Kadane 算法是一种动态规划算法,它的核心思想是:

  • 维护两个变量:curMax(当前结束位置的最大子数组和)和 globalMax(全局最大子数组和)。
  • 对于每个元素,我们有两种选择:
    1. 以当前元素作为新子数组的起点: 如果 nums[i]curMax + nums[i] 大,说明从当前元素开始新的子数组会得到更大的和。
    2. 将当前元素加入之前的子数组: 否则,将当前元素加入之前的子数组。

复杂度分析:

  • 时间复杂度:O(n),其中 n 是数组 nums 的长度。
  • 空间复杂度:O(1)。

代码实现(Java):

class Solution {
    public int maxSubArray(int[] nums) {
        int curMax = nums[0]; // 当前最大值
        int globalMax = nums[0]; // 全局最大值
        for (int i = 1; i < nums.length; i++) {
            // 对于每一个元素,要么新开一个子区间,要么加入已有的子区间
            curMax = Math.max(nums[i], curMax + nums[i]);
            // 更新全局最大值
            globalMax = Math.max(globalMax, curMax);
        }
        return globalMax;
    }
}

总结

今天的算法练习就到这里了。我们学习了如何使用 滑动窗口 解决 “拆炸弹” 和 “无重复字符的最长子串” 问题,以及如何使用 Kadane 算法 解决 “最大子数组和” 问题。希望大家有所收获!💪

相关文章:

  • 常见中间件漏洞攻略-Weblogic篇
  • Jenkins最新版,配置Gitee私人令牌和Gitee凭证
  • 2025年中国AI搜索的行业洞察报告
  • 如何使用SystemVerilog SVA检查跨时钟域信号?
  • C++多线程编程:从创建到管理的终极指南
  • VLAN章节学习
  • 万象更新(一)VTK 坐标轴、相机方向坐标轴、立方体坐标轴
  • 基于 C++ 类的程序设计模式与应用研究
  • Python个人学习笔记(19):模块(正则表达式)
  • JVM类加载过程详解
  • 浙江大学DeepSeek公开课第二季第二期将于下周一直播!
  • 【redis】主从复制:全量复制、部分复制、实时复制详解
  • extern关键字的用法
  • 机器人的手眼标定——机器人抓取系统基础系列(五)
  • Unity Animation的其中一种运用方式
  • 【吾爱出品】【阿修】兔子vCard editor by(通讯录编辑工具)
  • 【RH124】 第五章 创建、查看文本文件
  • Android Launcher3 HotSeat文件夹创建禁止方案全解析
  • WordPress二次开发中常用到的一些变量和函数
  • 算法刷题整理合集(六)
  • 北京发布今年第四轮拟供商品住宅用地清单,共计5宗22公顷
  • 五大国有银行明确将撤销监事会
  • 我国首部《人工智能气象应用服务办法》今天发布
  • 药明康德一季度净利增长89%,在手订单增超四成至523亿元
  • 中介在网上非法贩婴“一个孩子8.5万元”?丹阳警方介入
  • 俄外长与美国务卿通电话,讨论俄美关系及乌克兰问题