如何根据「数据范围」调整自己用什么算法 ...

简介: 如何根据「数据范围」调整自己用什么算法 ...

点击 这里 可以查看更多算法面试相关内容~


题目描述



这是 LeetCode 上的1004. 最大连续1的个数 III,难度为 Medium


给定一个由若干 0 和 1 组成的数组 A,我们最多可以将 K 个值从 0 变成 1 。


返回仅包含 1 的最长(连续)子数组的长度。


示例 1:


输入:
A = [1,1,1,0,0,0,1,1,1,1,0], 
K = 2
输出:
6
解释: 
[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
复制代码


示例 2:


输入:
A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], 
K = 3
输出:
10
解释:
[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。
复制代码


提示:


  • 1 <= A.length <= 20000
  • 0 <= K <= A.length
  • A[i] 为 0 或 1


动态规划解法(TLE)



看到本题,其实首先想到的是 DP,但是 DP 是 O(nk)O(nk)O(nk) 算法。


看到了数据范围是 10410^4104,那么时空复杂度应该都是 10810^8108


空间可以通过「滚动数组」优化到 10410^4104,但时间无法优化,会超时。


PS. 什么时候我们会用 DP 来解本题?通过如果 K 的数量级不超过 1000 的话,DP 应该是最常规的做法。


class Solution {
    public int longestOnes(int[] nums, int k) {
        int n = nums.length;
        // f(i,j) 代表考虑前 i 个数(并以 i 为结尾的),最大翻转次数为 j 时,连续 1 的最大长度
        int[][] f = new int[2][k + 1]; 
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= k; j++) {
                if (nums[i - 1] == 1) {
                    f[i & 1][j] = f[(i - 1) & 1][j] + 1;
                } else {
                    f[i & 1][j] = j == 0 ? 0 : f[(i - 1) & 1][j - 1] + 1;
                }
                ans = Math.max(ans, f[i & 1][j]);
            }
        }
        return ans;
    }
}
复制代码


  • 时间复杂度:O(nk)O(nk)O(nk)
  • 空间复杂度:O(k)O(k)O(k)


前缀和 + 二分 解法



从数据范围上分析,平方级别的算法过不了,往下优化就应该是对数级别的算法。


因此,很容易我们就会想到「二分」。


当然还需要我们对问题做一下等价变形。


最大替换次数不超过 k 次,可以将问题转换为找出连续一段区间 [l,r],使得区间中出现 0 的次数不超过 k 次。


我们可以枚举区间 左端点/右端点 ,然后找到其满足「出现 0 的次数不超过 k 次」的最远右端点/最远左端点。


为了快速判断 [l,r] 之间出现 0 的个数,我们需要用到前缀和。


假设 [l,r] 的区间长度为 len,区间和为 tot,那么出现 0 的格式为 len - tol,再与 k 进行比较。


由于数组中不会出现负权值,因此前缀和数组具有「单调性」,那么必然满足「其中一段满足 len - tol <= k,另外一段不满足 len - tol <= k」。


因此,对于某个确定的「左端点/右端点」而言,以「其最远右端点/最远左端点」为分割点的前缀和数轴,具有「二段性」。可以通过二分来找分割点。


class Solution {
    public int longestOnes(int[] nums, int k) {
        int n = nums.length;
        int ans = 0;
        int[] sum = new int[n + 1];
        for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1];
        for (int i = 0; i < n; i++) {
            int l = 0, r = i;
            while (l < r) {
                int mid = l + r >> 1;
                if (check(sum, mid, i, k)) {
                    r = mid;
                } else {
                    l = mid + 1;
                }
            }
            if (check(sum, r, i, k)) ans = Math.max(ans, i - r + 1);
        }
        return ans;
    }
    boolean check(int[] sum, int l, int r, int k) {
        int tol = sum[r + 1] - sum[l], len = r - l + 1;
        return len - tol <= k;
    }
}
复制代码


  • 时间复杂度:O(nlog⁡n)O(n\log{n})O(nlogn)
  • 空间复杂度:O(n)O(n)O(n)


关于二分结束后再次 check 的说明:由于「二分」本质是找满足某个性质的分割点,通常我们的某个性质会是「非等值条件」,不一定会取得 =


例如我们很熟悉的:从某个非递减数组中找目标值,找到返回下标,否则返回 -1。


当目标值不存在,「二分」找到的应该是数组内比目标值小或比目标值大的最接近的数。因此二分结束后先进行 check 再使用是一个好习惯。


双指针解法



由于我们总是比较 lentotk 三者的关系。


因此我们可以使用「滑动窗口」的思路,动态维护一个左右区间 [j, i] 和维护窗口内和 tot


右端点一直右移,左端点在窗口不满足「len - tol <= k」的时候进行右移。


即可做到线程扫描的复杂度:


class Solution {
    public int longestOnes(int[] nums, int k) {
        int n = nums.length;
        int ans = 0;
        for (int i = 0, j = 0, tot = 0; i < n; i++) {
            tot += nums[i];
            while ((i - j + 1) - tot > k) tot -= nums[j++];
            ans = Math.max(ans, i - j + 1);
        }
        return ans;
    }
}
复制代码


  • 时间复杂度:O(n)O(n)O(n)
  • 空间复杂度:O(1)O(1)O(1)


总结



除了掌握本题解法以外,我还希望你能理解这几种解法是如何被想到的(特别是如何从「动态规划」想到「二分」)。


根据数据范围(复杂度)调整自己所使用的算法的分析能力,比解决该题本身更加重要。


最后



这是我们「刷穿 LeetCode」系列文章的第 No.* 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先将所有不带锁的题目刷完。


在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。


由于 LeetCode 的题目随着周赛 & 双周赛不断增加,为了方便我们统计进度,我们将按照系列起始时的总题数作为分母,完成的题目作为分子,进行进度计算。当前进度为 */1916


为了方便各位同学能够电脑上进行调试和提交代码,我在 Github 建立了相关的仓库:github.com/SharingSour…


在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和一些其他的优选题解。

相关文章
|
4月前
|
存储 监控 算法
基于 C++ 哈希表算法实现局域网监控电脑屏幕的数据加速机制研究
企业网络安全与办公管理需求日益复杂的学术语境下,局域网监控电脑屏幕作为保障信息安全、规范员工操作的重要手段,已然成为网络安全领域的关键研究对象。其作用类似网络空间中的 “电子眼”,实时捕获每台电脑屏幕上的操作动态。然而,面对海量监控数据,实现高效数据存储与快速检索,已成为提升监控系统性能的核心挑战。本文聚焦于 C++ 语言中的哈希表算法,深入探究其如何成为局域网监控电脑屏幕数据处理的 “加速引擎”,并通过详尽的代码示例,展现其强大功能与应用价值。
99 2
|
5月前
|
数据采集 机器学习/深度学习 算法
别急着上算法,咱先把数据整明白:大数据分析的5个基本步骤,你都搞对了吗?
别急着上算法,咱先把数据整明白:大数据分析的5个基本步骤,你都搞对了吗?
206 4
|
2月前
|
存储 监控 算法
基于 Python 跳表算法的局域网网络监控软件动态数据索引优化策略研究
局域网网络监控软件需高效处理终端行为数据,跳表作为一种基于概率平衡的动态数据结构,具备高效的插入、删除与查询性能(平均时间复杂度为O(log n)),适用于高频数据写入和随机查询场景。本文深入解析跳表原理,探讨其在局域网监控中的适配性,并提供基于Python的完整实现方案,优化终端会话管理,提升系统响应性能。
72 4
|
3月前
|
机器学习/深度学习 算法
基于差分进化灰狼混合优化的SVM(DE-GWO-SVM)数据预测算法matlab仿真
本项目实现基于差分进化灰狼混合优化的SVM(DE-GWO-SVM)数据预测算法的MATLAB仿真,对比SVM和GWO-SVM性能。算法结合差分进化(DE)与灰狼优化(GWO),优化SVM参数以提升复杂高维数据预测能力。核心流程包括DE生成新种群、GWO更新位置,迭代直至满足终止条件,选出最优参数组合。适用于分类、回归等任务,显著提高模型效率与准确性,运行环境为MATLAB 2022A。
|
3月前
|
数据采集 算法 数据可视化
DROPP算法详解:专为时间序列和空间数据优化的PCA降维方案
DROPP(Dimensionality Reduction for Ordered Points via PCA)是一种专为有序数据设计的降维方法,通过结合协方差分析与高斯核函数调整,有效融入数据顺序特性。本文详细解析了DROPP的理论基础、实现步骤及其应用。算法核心在于利用相邻元素间的相似性特征,关注局部邻域信息以降低噪声影响,适用于时间序列或空间序列数据。文中通过模拟数据示例展示了算法的具体实现过程,并总结了其在气候研究和分子动力学等领域的广泛应用潜力。
126 0
DROPP算法详解:专为时间序列和空间数据优化的PCA降维方案
|
3月前
|
传感器 数据采集 人工智能
AI是如何收集体育数据的?从摄像头到算法,揭秘赛场背后的“数字间谍网“!
⚽ 你是否好奇:AI如何知道哈兰德每秒跑多快?教练的平板为何比裁判还清楚谁偷懒?本文揭秘AI收集体育数据的“黑科技”:视觉追踪、传感器网络、数据清洗与高阶分析。从高速摄像机捕捉梅西肌肉抖动,到GPS背心记录姆巴佩冲刺速度;从表情识别判断装伤,到量子计算模拟战术可能,AI正让体育更透明、精准。未来已来,2030年世界杯或将实现AI替代球探、裁判甚至教练!你认为AI数据收集算侵犯隐私吗?最想统计哪些奇葩指标?留言互动吧!
|
6月前
|
人工智能 编解码 算法
如何在Python下实现摄像头|屏幕|AI视觉算法数据的RTMP直播推送
本文详细讲解了在Python环境下使用大牛直播SDK实现RTMP推流的过程。从技术背景到代码实现,涵盖Python生态优势、AI视觉算法应用、RTMP稳定性及跨平台支持等内容。通过丰富功能如音频编码、视频编码、实时预览等,结合实际代码示例,为开发者提供完整指南。同时探讨C接口转换Python时的注意事项,包括数据类型映射、内存管理、回调函数等关键点。最终总结Python在RTMP推流与AI视觉算法结合中的重要性与前景,为行业应用带来便利与革新。
333 5
|
6月前
|
资源调度 算法 数据可视化
基于IEKF迭代扩展卡尔曼滤波算法的数据跟踪matlab仿真,对比EKF和UKF
本项目基于MATLAB2022A实现IEKF迭代扩展卡尔曼滤波算法的数据跟踪仿真,对比EKF和UKF的性能。通过仿真输出误差收敛曲线和误差协方差收敛曲线,展示三种滤波器的精度差异。核心程序包括数据处理、误差计算及可视化展示。IEKF通过多次迭代线性化过程,增强非线性处理能力;UKF避免线性化,使用sigma点直接处理非线性问题;EKF则通过一次线性化简化处理。
200 14
|
7月前
|
算法 图形学 数据安全/隐私保护
基于NURBS曲线的数据拟合算法matlab仿真
本程序基于NURBS曲线实现数据拟合,适用于计算机图形学、CAD/CAM等领域。通过控制顶点和权重,精确表示复杂形状,特别适合真实对象建模和数据点光滑拟合。程序在MATLAB2022A上运行,展示了T1至T7的测试结果,无水印输出。核心算法采用梯度下降等优化技术调整参数,最小化误差函数E,确保迭代收敛,提供高质量的拟合效果。
|
7月前
|
存储 监控 算法
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨
在数字化办公时代,公司监控上网软件成为企业管理网络资源和保障信息安全的关键工具。本文深入剖析C++中的链表数据结构及其在该软件中的应用。链表通过节点存储网络访问记录,具备高效插入、删除操作及节省内存的优势,助力企业实时追踪员工上网行为,提升运营效率并降低安全风险。示例代码展示了如何用C++实现链表记录上网行为,并模拟发送至服务器。链表为公司监控上网软件提供了灵活高效的数据管理方式,但实际开发还需考虑安全性、隐私保护等多方面因素。
105 0
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨

热门文章

最新文章