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

每日算法-250403

记录今天完成的几道 LeetCode 算法题。


713. 乘积小于 K 的子数组

题目

题目描述截图

思路

滑动窗口

解题过程

我们可以使用滑动窗口来解决这个问题。维护一个窗口 [left, right],并记录窗口内元素的乘积 sum

  1. 初始化 left = 0, right = 0, sum = 1, ret = 0
  2. right 指针向右遍历数组 nums
  3. 每次 right 移动,将 nums[right] 乘入 sum
  4. 使用 while 循环检查 sum 是否大于等于 k。如果 sum >= k,说明当前窗口不合法(乘积过大),需要缩小窗口。将 nums[left]sum 中除掉,并将 left 指针右移 (left++)。持续这个过程直到 sum < k 或者 left > right
  5. sum < k 时,以 right 结尾的所有子数组(从 nums[left..right]nums[right..right])的乘积都小于 k。这些子数组的数量等于当前窗口的长度 right - left + 1。将这个数量累加到结果 ret 中。
  6. right 指针继续移动,重复步骤 3-5,直到 right 到达数组末尾。

注意:while 循环的条件 left <= right 确保了即使单个元素 nums[right] 就大于等于 kleft 也能正确地移动到 right + 1,使得窗口长度为 0,不会错误地增加计数。

复杂度

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums 的长度。leftright 指针都最多移动 n n n 次。
  • 空间复杂度: O ( 1 ) O(1) O(1),只使用了常数级别的额外空间。

Code

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        // 处理 k <= 1 的情况,因为数组元素都是正数,乘积不可能小于等于 1(除非是空数组,但题目约束 nums.length >= 1)
        // 如果 k <= 1,任何正整数的乘积都不可能小于 k,所以结果为 0。
        if (k <= 1) {
            return 0;
        }
        int ret = 0, sum = 1;
        for (int left = 0, right = 0; right < nums.length; right++) {
            sum *= nums[right];
            // 当 sum >= k 时,需要缩小窗口
            while (sum >= k) {
                // 注意:因为上面已经判断 k > 1 且 nums[i] >= 1,所以 sum 不会是 0,可以安全地做除法
                // 同时,因为 sum >= k >= nums[left],所以 left 不会超过 right
                sum /= nums[left++];
            }
            // 此时窗口 [left, right] 内所有元素的乘积 < k
            // 以 right 结尾的有效子数组有 right - left + 1 个
            ret += (right - left + 1);
        }
        return ret;
    }
}

622. 设计循环队列

题目

题目描述截图

思路

使用数组模拟循环队列。

解题过程

  1. 使用一个固定大小的数组 elem 来存储队列元素。
  2. length 记录数组容量(即队列容量 k)。
  3. size 记录当前队列中的元素个数。
  4. head 指向队首元素的索引。
  5. tail 指向下一个待插入位置的索引。
  6. 循环的关键:当 headtail 需要移动时,使用取模运算 % length 来实现循环。例如,入队后 tail = (tail + 1) % length,出队后 head = (head + 1) % length
  7. 判空/判满:队列为空的条件是 size == 0;队列已满的条件是 size == length
  8. 获取队尾元素 Rear():队尾元素实际存储在 tail 指向位置的前一个索引。为了正确处理 tail 为 0 时(此时逻辑上的队尾在数组末尾 length - 1 处)的情况,使用 (tail - 1 + length) % length 来计算队尾元素的实际索引。

复杂度

  • 时间复杂度: 初始化和各个方法(enQueue, deQueue, Front, Rear, isEmpty, isFull)都是 O ( 1 ) O(1) O(1)
  • 空间复杂度: O ( k ) O(k) O(k),需要一个大小为 k k k 的数组来存储队列元素。

Code

class MyCircularQueue {

    private int[] elem; // 存储元素的数组
    private int head;   // 队首指针(索引)
    private int tail;   // 队尾指针(下一个要插入的位置的索引)
    private int size;   // 当前队列中的元素数量
    private int length; // 队列容量

    public MyCircularQueue(int k) {
        length = k;
        elem = new int[length];
        head = 0;
        tail = 0;
        size = 0;
    }

    // 入队操作
    public boolean enQueue(int value) {
        if (isFull()) { // 队列已满,无法入队
            return false;
        }
        elem[tail] = value; // 在队尾指针处放入元素
        tail = (tail + 1) % length; // 队尾指针后移(循环)
        size++; // 元素数量增加
        return true;
    }

    // 出队操作
    public boolean deQueue() {
        if (isEmpty()) { // 队列为空,无法出队
            return false;
        }
        // 队首元素不需要显式移除,只需移动 head 指针
        head = (head + 1) % length; // 队首指针后移(循环)
        size--; // 元素数量减少
        return true;
    }

    // 获取队首元素
    public int Front() {
        if (isEmpty()) {
            return -1; // 队列为空
        }
        return elem[head]; // 返回队首指针处的元素
    }

    // 获取队尾元素
    public int Rear() {
        if (isEmpty()) {
            return -1; // 队列为空
        }
        // 队尾元素在 tail 指针的前一个位置
        // 需要处理 tail = 0 的情况,此时队尾在数组末尾
        int rearIndex = (tail - 1 + length) % length;
        return elem[rearIndex];
    }

    // 检查队列是否为空
    public boolean isEmpty() {
        return size == 0;
    }

    // 检查队列是否已满
    public boolean isFull() {
        return size == length;
    }
}

/**
 * Your MyCircularQueue object will be instantiated and called as such:
 * MyCircularQueue obj = new MyCircularQueue(k);
 * boolean param_1 = obj.enQueue(value);
 * boolean param_2 = obj.deQueue();
 * int param_3 = obj.Front();
 * int param_4 = obj.Rear();
 * boolean param_5 = obj.isEmpty();
 * boolean param_6 = obj.isFull();
 */

2904. 最短且字典序最小的美丽子字符串 (复习)

题目

题目描述截图

思路回顾

这次复习完美解决了问题。核心思路仍然是滑动窗口,找到所有包含 k 个 ‘1’ 的子串,然后在这些子串中比较长度和字典序。

详细的题解请看之前的博文:每日算法-250329

代码

class Solution {
    public String shortestBeautifulSubstring(String s, int k) {
        String retString = "";
        int n = s.length();
        
        int retLen = n + 1;
        int count = 0;

        for (int left = 0, right = 0; right < n; right++) {
            char in = s.charAt(right);
            // 进窗口
            if (in == '1') {
                count++;
            }

            // 判断与收缩
            while (count >= k) {
                if (count == k) {
                   int len = right - left + 1;
                   // 更新结果
                   String currentSub = s.substring(left, right + 1);
                   if (len < retLen) {
                       retLen = len;
                       retString = currentSub;
                   } else if (len == retLen) {
                       if (retString.isEmpty() || currentSub.compareTo(retString) < 0) {
                           retString = currentSub;
                       }
                   }
                }

                // 出窗口
                if (s.charAt(left) == '1') {
                    count--;
                }
                left++;
            }
        }

        return (retLen == n + 1) ? "" : retString;
    }
}


1234. 替换子串得到平衡字符串 (复习)

题目

题目描述截图

思路回顾

这次复习时遇到了一个小问题:忘记处理输入字符串本身就已经平衡的情况(即每个字符出现次数都等于 n/4)。如果一开始就平衡,应该直接返回 0。不处理这个边界情况,会导致后续滑动窗口的 while 循环条件一直满足(因为所有字符的计数都 <= m),使得 left 指针不断自增,最终可能导致数组越界。

核心思路是滑动窗口,目标是找到一个最短的子串,替换掉这个子串后,原字符串中剩余部分的 Q, W, E, R 字符数量都不超过 n/4。这等价于找到一个最短的窗口 [left, right],使得窗口外的字符计数满足平衡条件。

详细的题解请看之前的博文:每日算法-250330

代码

class Solution {
    public int balancedString(String ss) {
        char[] s = ss.toCharArray();
        int n = s.length;
        int ret = n + 1, m = n / 4;
        int[] hash = new int[128];
        for (char c : s) {
            hash[c]++;
        }
        if (hash['Q'] == m && hash['W'] == m && hash['E'] == m && hash['R'] == m) {
            return 0;
        }
        for (int left = 0, right = 0; right < n; right++) {
            hash[s[right]]--;
            while (hash['Q'] <= m && hash['W'] <= m && hash['E'] <= m && hash['R'] <= m) {
                ret = Math.min(ret, right - left + 1);
                hash[s[left++]]++;
            }
        }
        return ret;
    }
}

http://www.dtcms.com/a/108702.html

相关文章:

  • 20.IP协议
  • spring-ai-alibaba第七章阿里dashscope集成RedisChatMemory实现对话记忆
  • 行式、 列式数据库
  • Python | 使用Matplotlib绘制Swarm Plot(蜂群图)
  • 2025 AI智能数字农业研讨会在苏州启幕,科技助农与数据兴业成焦点
  • OpenRouter开源的AI大模型路由工具,统一API调用
  • 【3.软件工程】3.6 W开发模型
  • 【Python算法】双指针算法
  • 【力扣hot100题】(049)二叉树中的最大路径和
  • go mod download报错解决方案
  • 原生 AJAX
  • fastAPI详细介绍以及使用方法
  • Python数据可视化-第6章-坐标轴的定制
  • (学习总结32)Linux 基础 IO
  • AUTOSAR_LIN_Interface_Detailed
  • Windows 图形显示驱动开发-WDDM 2.1 功能(四)
  • 追踪大型语言模型的思维过程:提示词工程重要
  • 高效创建工作流,可实现类似unreal engine的蓝图效果,内部使用多线程高效执行节点函数
  • Oracle触发器使用(一):DML触发器
  • 高可用的Hadoop完全分布式环境搭建
  • 10分钟私有部署QwQ-32B模型,像购买Ecs实例一样快捷
  • 机器学习-聚类分析算法
  • P10914 [蓝桥杯 2024 国 B] 跳石
  • python 爬取网站图片的小demo
  • 【AI论文】CodeARC:评估归纳程序合成中大语言模型代理的推理能力基准
  • Xilinx FPGA XCVC1902-2MSEVSVA2197 Versal AI Core系列芯片的详细介绍
  • Java中的方法重载
  • 第J3-1周:DenseNet算法 实现乳腺癌识别(含真实图片预测)
  • 牛客练习题——素数(质数)
  • 策略模式实际用处,改吧改吧直接用,两种方式