【每日算法Day 107】面试必考:良心推荐,一题三解,不看后悔一辈子

简介: 【每日算法Day 107】面试必考:良心推荐,一题三解,不看后悔一辈子

可能有些同学只会写 python ,看不懂 c++。但是一个是因为我懒,多解时不想再写一遍 python 了,一个是理解算法最重要,语言不重要。今天学妹发来一张图,我觉得说的很好。

院长大大如是说

题目链接

LeetCode 1248. 统计「优美子数组」[1]

题目描述

给你一个整数数组 nums 和一个整数 k

如果某个连续子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。

请返回这个数组中「优美子数组」的数目。

示例1

输入:
nums = [1,1,2,1,1], k = 3
输出:
2
解释:
包含 3 个奇数的子数组是 [1,1,2,1] 和 [1,2,1,1] 。

示例2

输入:
nums = [2,4,6], k = 1
输出:
0
解释:
数列中不包含任何奇数,所以不存在优美子数组。

示例3

输入:
nums = [2,2,2,1,2,2,1,2,2,2], k = 2
输出:
16

题解

为了方便表示,我们下面统一将奇数变为 1 ,偶数变为 0 ,不难发现这是等价于原来题意的。

统计奇数位置

我们发现,如果两个 1 之间(包含自身)一共包含了 k1 ,那么这 k1 可以构成的连续子数组个数就是 左边 0 的个数加一 乘上 右边 0 的个数加一

那么如何统计每个 1 前后 0 的个数呢?其实只需要记录一下每个 1 的位置,然后直接用相邻两个 1 的位置相减就可以得到中间 0 的个数加一了。

所以直接记录每个 1 的位置,为了处理边界,我们还需要在最开始添加上虚拟位置 -1 ,在最后添加虚拟位置 n

然后对于第  个 1 来说,如果将它作为子数组第一个 1 ,那么最后一个 1 应该是第  个 1 。所以直接计算两边 0 的数量,再加一相乘就行了:

最后遍历所有的 i ,将第  个 1 作为起点,然后累加答案就行了。

时间复杂度  ,空间复杂度  。

双指针

主要思想还是跟上面方法一样,但是不用直接计算 左边 0 的个数加一 乘上 右边 0 的个数加一。只需要计算 左边 0 的个数加一 ,然后右指针如果指着 0 ,就加上这个值,一直加到右指针为 1 为止。

所以只需要用常数个变量就行了,even 记录子数组前面有多少个 0cnt 记录当前子数组有多少个 1 。用 l 指向子数组开头,r 指向子数组结尾。

如果 cnt = k ,那就说明子数组中正好有 k1 。那就右移 l ,直到遇到 1 为止,这样就能统计出左边有多少个 0 ,记录在 even 中。然后 l 右移跳过这个 1 ,同时 cnt 减一。

如果 cnt < k ,那就说明 1 的数量不够,r 继续右移就行了。同时每移动一次,答案都要加上 even 值,因为你之前 cnt = k 时记录了一下左边 0 的数量,现在右边每一个 0 都得加上它。其实除了初始阶段,其余时候 cnt 都是等于 k-1 的。而初始阶段 even = 0 ,所以加上也没事,可以合并写。

时间复杂度  ,空间复杂度  。

前缀和

遍历原数组中每个位置 ,如果  之前(包含自身) 1 的个数一共 odd 个(也就是前缀和),那么我们只需要看有多少个位置  满足 1 的前缀和等于 odd-k ,那么  就是正好包含 k1 的子数组。

所以我们只需要用一个计数数组来记录一下前缀和对应的出现次数就行了,然后每次取出 odd-k 的次数加到答案里就行了。

时间复杂度  ,空间复杂度  。

代码

统计奇数位置(c++)

class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> pos;
        pos.push_back(-1);
        for (int i = 0; i < n; ++i) {
            if (nums[i]&1) pos.push_back(i);
        }
        pos.push_back(n);
        int res = 0, sz = pos.size();
        for (int i = 1; i+k < sz; ++i) {
            res += (pos[i] - pos[i-1]) * (pos[i+k] - pos[i+k-1]);
        }
        return res;
    }
};

双指针(c++)

class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        int res = 0, cnt = 0, even = 0;
        int l = 0, r = 0;
        while (r < n) {
            if (cnt < k && (nums[r++]&1)) cnt++;
            if (cnt == k) {
                even = 1;
                while (!(nums[l++]&1)) even++;
                cnt--;
            }
            res += even;
        }
        return res;
    }
};

前缀和(c++)

class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> count(n+1, 0);
        count[0] = 1;
        int res = 0, odd = 0;
        for (int i = 0; i < n; ++i) {
            odd += nums[i]&1;
            if (odd >= k) res += count[odd-k];
            count[odd]++;
        }
        return res;
    }
};
相关文章
|
24天前
|
存储 调度 C++
【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)
【操作系统】进程与线程的区别及总结(非常非常重要,面试必考题,其它文章可以不看,但这篇文章最后的总结你必须要看,满满的全是干货......)
48 1
|
5天前
|
算法 Java
Java面试题:解释垃圾回收中的标记-清除、复制、标记-压缩算法的工作原理
Java面试题:解释垃圾回收中的标记-清除、复制、标记-压缩算法的工作原理
13 1
|
1月前
|
消息中间件 算法 Java
抖音面试:说说延迟任务的调度算法?
Netty 框架是以性能著称的框架,因此在它的框架中使用了大量提升性能的机制,例如 Netty 用于实现延迟队列的时间轮调度算法就是一个典型的例子。使用时间轮调度算法可以实现海量任务新增和取消任务的时间度为 O(1),那么什么是时间轮调度算法呢?接下来我们一起来看。 ## 1.延迟任务实现 在 Netty 中,我们需要使用 HashedWheelTimer 类来实现延迟任务,例如以下代码: ```java public class DelayTaskExample { public static void main(String[] args) { System.ou
32 5
抖音面试:说说延迟任务的调度算法?
|
5天前
|
算法 Java 程序员
Java面试题:解释Java的垃圾回收机制,包括常见的垃圾回收算法。介绍一下Java的垃圾回收算法中的标记-压缩算法。
Java面试题:解释Java的垃圾回收机制,包括常见的垃圾回收算法。介绍一下Java的垃圾回收算法中的标记-压缩算法。
8 0
|
5天前
|
算法 Java 开发者
Java面试题:Java内存探秘与多线程并发实战,Java内存模型及分区:理解Java堆、栈、方法区等内存区域的作用,垃圾收集机制:掌握常见的垃圾收集算法及其优缺点
Java面试题:Java内存探秘与多线程并发实战,Java内存模型及分区:理解Java堆、栈、方法区等内存区域的作用,垃圾收集机制:掌握常见的垃圾收集算法及其优缺点
8 0
|
5天前
|
存储 算法 Java
Java面试题:解释JVM的内存结构,并描述堆、栈、方法区在内存结构中的角色和作用,Java中的多线程是如何实现的,Java垃圾回收机制的基本原理,并讨论常见的垃圾回收算法
Java面试题:解释JVM的内存结构,并描述堆、栈、方法区在内存结构中的角色和作用,Java中的多线程是如何实现的,Java垃圾回收机制的基本原理,并讨论常见的垃圾回收算法
7 0
|
25天前
|
存储 算法 Java
面试高频算法题汇总「图文解析 + 教学视频 + 范例代码」之 二分 + 哈希表 + 堆 + 优先队列 合集
面试高频算法题汇总「图文解析 + 教学视频 + 范例代码」之 二分 + 哈希表 + 堆 + 优先队列 合集
|
2月前
|
机器学习/深度学习 编解码 算法
算法工程师面试问题总结 | YOLOv5面试考点原理全解析
本文给大家带来的百面算法工程师是深度学习目标检测YOLOv5面试总结,文章内总结了常见的提问问题,旨在为广大学子模拟出更贴合实际的面试问答场景。在这篇文章中,我们还将介绍一些常见的深度学习目标检测面试问题,并提供参考的回答及其理论基础,以帮助求职者更好地准备面试。通过对这些问题的理解和回答,求职者可以展现出自己的深度学习目标检测领域的专业知识、解决问题的能力以及对实际应用场景的理解。同时,这也是为了帮助求职者更好地应对深度学习目标检测岗位的面试挑战,提升面试的成功率和竞争力。
|
2月前
|
机器学习/深度学习 算法 固态存储
深度学习算法工程师面试问题总结| 深度学习目标检测岗位面试总结
本文给大家带来的百面算法工程师是深度学习目标检测岗位面试总结,文章内总结了常见的提问问题,旨在为广大学子模拟出更贴合实际的面试问答场景。在这篇文章中,我们还将介绍一些常见的深度学习目标检测面试问题,并提供参考的回答及其理论基础,以帮助求职者更好地准备面试。通过对这些问题的理解和回答,求职者可以展现出自己的深度学习目标检测领域的专业知识、解决问题的能力以及对实际应用场景的理解。同时,这也是为了帮助求职者更好地应对深度学习目标检测岗位的面试挑战,提升面试的成功率和竞争力。
|
1月前
|
数据采集 算法 数据挖掘
LeetCode 题目 80:删除排序数组中的重复项 II【算法面试高频题】
LeetCode 题目 80:删除排序数组中的重复项 II【算法面试高频题】