”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 面试工具包:数组 - 滑动窗口。

面试工具包:数组 - 滑动窗口。

发布于2024-11-06
浏览:151

一切都与模式有关!

一旦你学会了这些模式,一切都开始变得更容易了!如果你像我一样,你可能不喜欢技术面试,我不怪你——面试可能很艰难。

数组问题是面试中最常见的问题。这些问题通常涉及使用自然数组:

const arr = [1, 2, 3, 4, 5];

还有字符串问题,本质上是字符数组:

"mylongstring".split(""); // ['m', 'y', 'l', 'o','n', 'g', 's','t','r','i', 'n', 'g']


解决数组问题最常见的模式之一是滑动窗口

滑动窗口模式

滑动窗口模式涉及两个向同一方向移动的指针,就像在数组上滑动的窗口一样。

何时使用它

当您需要查找满足特定条件(例如最小值、最大值、最长或)的子数组子字符串时,请使用滑动窗口模式最短。

规则1:如果需要查找子数组或子字符串,并且数据结构是数组或字符串,请考虑使用滑动窗口模式。

简单的例子

下面通过一个基本例子来介绍滑动窗口中指针的概念:

function SlidingWindow(arr) {
    let l = 0;  // Left pointer
    let r = l   1;  // Right pointer

    while (r 



注意左(L)和右(R)指针不必同时移动,但必须朝同一方向移动。

Interview Kit: Arrays - Sliding window.

右指针永远不会低于左指针。

让我们通过一个真实的面试问题来探讨这个概念。

现实问题:不重复字符的最长子串

问题:给定一个字符串s,求不重复字符的最长子串的长度。

关键字: -字符串,最长(最大)

function longestSubstr(str) {
    let longest = 0;
    let left = 0;
    let hash = {};

    for (let right = 0; right = left) {
            left = hash[str[right]]   1;
        }

        hash[str[right]] = right;
        longest = Math.max(longest, right - left   1);
    }

    return longest;
}

如果这看起来很复杂,请不要担心——我们会一点一点地解释它。

let str = "helloworld";
console.log(longestSubstr(str));  // Output: 5

这个问题的核心是找到最长的子串没有重复字符。

初始窗口:大小 0

开始时,左(L)和右(R)指针都在同一个位置:

let left = 0;

for (let right = 0; right 





h e l l o w o r l d 
^
^
L
R

我们有一个空的哈希(对象):

let hash = {};

物体有什么伟大之处?它们存储唯一的密钥,这正是我们解决这个问题所需要的。我们将使用哈希来跟踪我们访问过的所有字符,并检查我们之前是否见过当前字符(以检测重复项)。

通过查看字符串,我们可以直观地看出world是最长的没有重复字符的子串:

h e l l o w o r l d 
          ^        ^   
          L        R

这个长度为 5。那么,我们如何到达那里?

我们一步步分解:

初始状态

hash = {}

h e l l o w o r l d 
^
^
L
R

迭代 1:

在每次迭代中,我们将 R 指针下的字符添加到哈希映射中并递增:

hash[str[right]] = right;
longest = Math.max(longest, right - left   1);

目前,我们的窗口中没有重复字符(h和e):

hash = {h: 0}
h e l l o w o r l d 
^ ^
L R

迭代 2:

hash = {h: 0, e: 1}
h e l l o w o r l d 
^   ^
L   R

现在,我们有一个新窗口:hel。

迭代 3:

hash = {h: 0, e: 1, l: 2}
h e l l o w o r l d 
^     ^
L     R

这就是有趣的地方:我们的哈希中已经有 l,而 R 指向字符串中的另一个 l。这就是我们的 if 语句出现的地方:

if (hash[str[right]] !== undefined)

如果我们的散列包含 R 指向的字母,我们就发现了一个重复项!上一个窗口 (hel) 是我们迄今为止最长的窗口。

那么,我们下一步该做什么?因为我们已经处理了左子串,所以我们通过向上移动 L 指针来从左侧缩小窗口。但我们要把 L 移动多远?

left = hash[str[right]]   1;

我们将 L 移到重复项之后:

hash = {h: 0, e: 1, l: 2}
h e l l o w o r l d 
      ^
      ^
      L
      R

我们仍然将重复项添加到哈希中,因此 L 现在的索引为 3。

hash[str[right]] = right;
longest = Math.max(longest, right - left   1);

新状态:迭代 4

hash = {h: 0, e: 1, l: 3}
h e l l o w o r l d 
      ^ ^
      L R

迭代 4 至 6

hash = {h: 0, e: 1, l: 3, o: 4, w: 5}
h e l l o w o r l d 
      ^     ^
      L     R

当 R 指向另一个重复项 (o) 时,我们将 L 移到第一个 o 之后:

hash = {h: 0, e: 1, l: 3, o: 4, w: 5}
h e l l o w o r l d 
          ^ ^
          L R

我们继续,直到遇到另一个重复的 l:

hash = {h: 0, e: 1, l: 3, o: 4, w: 5, o: 6, r: 7}
h e l l o w o r l d 
          ^     ^
          L     R

但是请注意它在当前窗口之外!从w开始,

规则 3:忽略已处理的子 x

当前窗口之外的任何内容都是无关紧要的——我们已经处理了它。管理这个的关键代码是:

if (hash[str[right]] !== undefined && hash[str[right]] >= left)

这个条件确保我们只关心当前窗口内的字符,过滤掉任何噪音。

hash[str[right]] >= left

我们关注任何大于或等于左指针的东西

最终迭代:

hash = {h: 0, e: 1, l: 8, o: 4, w: 5, o: 6, r: 7}
h e l l o w o r l d 
          ^       ^
          L       R

我知道这很详细,但是将问题分解为更小的模式或规则是掌握它们的最简单方法。

总之:

  • 规则1:问题中的关键词(例如最大值、最小值)是线索。本题是求最长的子串不带重复字符。
  • 规则 2: 如果您需要查找唯一或不重复的元素,请考虑哈希映射。
  • 规则 3: 关注当前窗口——窗口之外的任何内容都是无关紧要的。

奖金提示:

  • 使用一个小子集分解问题并使其变得冗长。
  • 最大化当前窗口时,考虑如何使其尽可能长。反之,最小化的时候,想想如何让它尽可能的小。

最后,这里有一个小挑战供您尝试!我将在评论中发布我的解决方案 - 这是一种很好的练习方式。

问题 2:总和大于或等于目标

给定一个数组,找到总和等于或大于目标的最小子数组(我的解决方案将是第一个注释)。

/**
 * 
 * @param {Array} arr 
 * @param {number} target 
 * @returns {number} - length of the smallest subarray
 */
function greaterThanOrEqualSum(arr, target){
   let minimum = Infinity;
   let left = 0;
   let sum = 0;

   // Your sliding window logic here!
}

记住,就像编程中的任何事情一样,重复是关键!滑动窗口问题总是会出现,所以不要犹豫,谷歌更多的例子并继续练习。

我的这篇文章很简短,但请继续关注——下一篇文章将深入探讨两指针模式和递归(为树问题做准备)。这将会更具挑战性!

如果您想要更多独家内容,您可以在 Twitter 或 Ko-fi 上关注我,我会在那里发布一些额外的内容!

资源:

技术面试手册

leet代码数组101

版本声明 本文转载于:https://dev.to/sfundomhlungu/interview-kit-arrays-sliding-window-9hd?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何将列表列表转换为统一的 NumPy 数组?
    如何将列表列表转换为统一的 NumPy 数组?
    将列表列表转换为 NumPy 数组数据分析中的一个常见任务是将列表列表转换为 NumPy 数组高效的数值运算。该数组可以通过将每个列表分配给一行来形成,列表中的每个元素占据一列。选项 1:数组数组如果子列表具有不同的长度,合适的方法是创建数组的数组。这保留了列表列表的原始结构,从而可以轻松检索特定元...
    编程 发布于2024-11-06
  • 前端的顶级设计模式
    前端的顶级设计模式
    在过去的几个月里,我为前端开发人员分享了一些流行的设计模式。其中包括 Singleton、Facade、Observer、Publisher/Subscriber 等模式。今天,我想总结一下这些模式的一些要点和好处,以及如何使用它们来改进您的前端开发流程。 什么是设计模式 设计模式是...
    编程 发布于2024-11-06
  • ServBay版本.pdate公告
    ServBay版本.pdate公告
    我们很高兴地宣布新版本 1.4.4 已经到来!让我们来看看新增的备受期待的新功能。 新功能 CA和证书管理: 统一SSL证书管理平台:全新的证书管理平台,旨在简化证书申请和管理流程。 ServBay User CA 和 ServBay Public CA: 引入 Se...
    编程 发布于2024-11-06
  • Spring框架中的控制反转
    Spring框架中的控制反转
    控制反转(IoC)和依赖注入(DI)是Spring框架中的两个基本概念。传统上,对象负责创建和管理它们自己的依赖关系。然而,IoC 通过将对象创建和依赖管理的控制权移交给像 Spring 这样的框架来翻转这一责任。 这种转变有几个优点: 更简单的实现交换:只需对代码库进行最小的更改即可交换不同的实现...
    编程 发布于2024-11-06
  • 使用 React 构建递归文件系统:深入探讨
    使用 React 构建递归文件系统:深入探讨
    简介:在 React 中构建递归文件系统 在现代 Web 开发中,创建交互式动态文件系统是一个常见的要求。无论是管理文档、组织项目还是构建复杂的数据结构,拥有强大的文件系统都至关重要。在这篇博文中,我们将探讨如何在 React 中构建递归文件系统,重点关注可以添加、重命名或删除的嵌...
    编程 发布于2024-11-06
  • SQL 查询速度慢?使用此技术提高应用程序的性能
    SQL 查询速度慢?使用此技术提高应用程序的性能
    挑战 在我的应用程序(React Spring Boot Oracle)中,处理大型数据集导致处理时间极其缓慢。我需要一种解决方案来提高性能而不影响准确性或完整性。 解决方案:NTILE 并行处理 NTILE 是一个功能强大的 SQL 窗口函数,旨在将结果集划分为指...
    编程 发布于2024-11-06
  • 关于测试覆盖率的真相
    关于测试覆盖率的真相
    一个强有力的真理。 看下面这段简单明了的代码: function sum(a, b) { return a b; } 现在,让我们为它编写一些测试: test('sum', () => { expect(sum(1, 2)).toBe(3); expect(s...
    编程 发布于2024-11-06
  • 为什么我的 OpenGL 三角形无法在 Go 中渲染?调查顶点缓冲区问题。
    为什么我的 OpenGL 三角形无法在 Go 中渲染?调查顶点缓冲区问题。
    Go 中的 OpenGL 顶点缓冲区问题在尝试在 Go 中使用 OpenGL 显示三角形时,用户遇到了顶点缓冲区问题缓冲区无法渲染形状。 Go 代码源自教程,但与 C 代码不同的是,它没有产生任何输出。问题原因问题的根本原因位于传递给 vertexAttrib.AttribPointer() 的参数...
    编程 发布于2024-11-06
  • 为什么在 Linux 32 位发行版上的 Go 程序中设置 `ulimit -n` 会导致“参数无效”错误?
    为什么在 Linux 32 位发行版上的 Go 程序中设置 `ulimit -n` 会导致“参数无效”错误?
    如何在 Go 程序中设置 ulimit -n?问题用户尝试在 Go 程序中设置 ulimit -n使用 setrlimit 和 getrlimit 系统调用将其限制在程序内而不是全局。然而,在尝试设置该值时出现错误,提示“参数无效”。解决方案发现该问题是由于 Linux 32 的 Getrlimit...
    编程 发布于2024-11-06
  • 如何在Python中创建无限深度的动态嵌套字典?
    如何在Python中创建无限深度的动态嵌套字典?
    未定义深度的动态嵌套字典在涉及复杂多级数据结构的场景中,经常会遇到变量嵌套字典的需求水平。虽然硬编码插入语句是一种潜在的解决方案,但当事先未知嵌套深度时,这种方法是不切实际的。要克服此限制,请考虑利用 Python 的 collections.defaultdict,它允许动态创建字典。可以使用以下...
    编程 发布于2024-11-06
  • Python 变得强大:轻松编程的初学者指南
    Python 变得强大:轻松编程的初学者指南
    Python 是一门强大的编程语言,语法简单,应用广泛。安装 Python 后,可以学习其基本语法,包括变量赋值、数据类型和流程控制。实战案例中,我们通过蒙特卡罗模拟计算圆周率,展示了 Python 在数值计算中的能力。此外,Python 拥有丰富的库,涵盖机器学习、数据分析和网络开发等领域,体现了...
    编程 发布于2024-11-06
  • 如何在没有 jQuery 的情况下监听动态创建的元素的事件?
    如何在没有 jQuery 的情况下监听动态创建的元素的事件?
    在没有 jQuery 的情况下监听动态创建的元素的事件使用外部页面时,向动态生成的元素添加事件监听器可能具有挑战性。在这种情况下,委派事件处理至关重要。一种方法是使用 event.target 属性来检查单击或触发的元素是否属于所需类型。这是一个示例:document.querySelector('...
    编程 发布于2024-11-06
  • 利用 Snipbyte 的高级考勤管理系统优化劳动力效率
    利用 Snipbyte 的高级考勤管理系统优化劳动力效率
    在当今的商业环境中,有效管理员工出勤、轮班和工资单可以决定组织的成功或失败。准确的考勤跟踪不仅可以确保运营顺利进行,还有助于提高生产力。在 Snipbyte,我们专注于提供一流的基于网络的解决方案来增强业务流程,包括我们的高级考勤管理系统。 为什么选择Snipbyte的考勤管理系统? 我们的考勤管理...
    编程 发布于2024-11-06
  • Laravel Auth 路由教程
    Laravel Auth 路由教程
    Laravel auth routes is one of the essential features of the Laravel framework. Using middlewares you can implement different authentication strategies...
    编程 发布于2024-11-06
  • 如何有效地跳转到大型文本文件中的特定行?
    如何有效地跳转到大型文本文件中的特定行?
    优化大型文本文件中的跳行:另一种方法处理具有不同长度行的大量文本文件时,通常效率很低顺序读取每一行以达到特定的行号。问题中提供的代码示例说明了这种方法,需要对整个文件进行可能缓慢的迭代。然而,还有一种替代方法可以通过利用计算出的偏移列表来优化跳线。基于偏移的跳线要克服这一挑战,需要一种更有效的方法涉...
    编程 发布于2024-11-06

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3