0. 内容说明

最近在自己编写一些小的算法的时候,深感自己的算法过于臃肿。碰巧Datawhale在新的一期组队学习中组织了数据结构与算法的课程学习。于是就参加了,再次感谢Datawhale~~

首先跟大家分享一下两个自己感觉比较好的学习资料,一个是 算法通关手册 ,也是Datawhale在本次组队学习中的学习资料;一个是B站上的视频 【北京大学】数据结构与算法Python版(完整版),老师讲的特别棒(也难得有Python版的数据结构课程,哈哈~)。

需要指出的是:本次博客的内容更像是对上述两个资料做的笔记,很多都是资料上的原内容,并非原创。

1. 双指针简介

双指针(Two Pointers):指的是在遍历元素的过程中,不是使用单个指针进行访问,而是使用两个指针进行访问,从而达到相应的目的。如果两个指针方向相反,则称为对撞时针。如果两个指针方向相同,则称为快慢指针。如果两个指针分别属于不同的数组 / 链表,则称为分离双指针。在数组的区间问题上,暴力算法的时间复杂度往往是 O ( n 2 ) O(n^2) O(n2)。而双指针利用了区间「单调性」的性质,从而将时间复杂度降到了 O ( n ) O(n) O(n)。

1.1 对撞指针

对撞指针算法中有一个left指针,有一个right指针。left、right 分别指向序列第一个元素和最后一个元素,然后 left 指针不断递增,right 不断递减,直到两个指针的值相撞(即 left == right),或者满足其他要求的特殊条件为止。

例题1

  • 题目

    给定一个升序排列的整数数组:numbers 和一个目标值 target。

  • 要求

    从数组中找出满足相加之和等于 target 的两个数,并返回两个数在数组 中下的标值。

  • 思路

    使用两个指针 left,right。left 指向数组第一个值最小的元素位置,right 指向数组值最大元素位置。判断两个位置上的元素的和与目标值的关系。如果元素和等于目标值,则返回两个元素位置。如果元素和大于目标值,则让 right 左移,继续检测。如果元素和小于目标值,则让 left 右移,继续检测。直到 left 和 right 移动到相同位置停止检测。如果最终仍没找到,则返回 [-1, -1]。

代码:

class Solution:def twoSum(self, numbers: List[int], target: int) -> List[int]:left = 0right = len(numbers) - 1while left < right:total = numbers[left] + numbers[right]if total == target:return [left + 1, right + 1]elif total < target:left += 1else:right -= 1return [-1, -1]

例题2

  • 题目

    验证回文串。

  • 要求

    判断是否为回文串(只考虑字符串中的字母和数字字符,并且忽略字母的大小写)。

  • 思路

    使用两个指针 left,right。left 指向字符串开始位置,right 指向字符串结束位置。判断两个指针对应字符是否是字母或数字。 通过 left 右移、right 左移的方式过滤掉字母和数字以外的字符。然后判断 s[start] 是否和 s[end] 相等(注意大小写)。如果相等,则将 left 右移、right 左移,继续进行下一次过滤和判断。如果不相等,则说明不是回文串,直接返回 False。如果遇到 left == right,跳出循环,则说明该字符串是回文串,返回 True。

代码:

class Solution:def isPalindrome(self, s: str) -> bool:left = 0right = len(s) - 1while left < right:if not s[left].isalnum():left += 1continueif not s[right].isalnum():right -= 1continueif s[left].lower() == s[right].lower():left += 1right -= 1else:return Falsereturn True

例题3

  • 题目

    给定 n 个非负整数 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1​,a2​,...,an​,每个数代表坐标中的一个点 ( i , a i ) (i,a_i) (i,ai​)。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 ( i , a i ) (i,a_i) (i,ai​) 和 ( i , 0 ) (i,0) (i,0)。

  • 要求

    找出其中的两条垂直线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

  • 思路

    使用两个指针 left,right。left 指向数组开始位置,right 指向数组结束位置。计算 left 和 right 所构成的面积值,同时维护更新最大面积值。判断 left 和 right 的高度值大小。如果 left 指向的直线高度比较低,则将 left 指针右移。如果 right 指向的直线高度比较低,则将 right 指针左移。如果遇到 left == right,跳出循环,最后返回最大的面积。

代码:

class Solution:def maxArea(self, height: List[int]) -> int:left = 0right = len(height) - 1ans = 0while left < right:area = min(height[left], height[right]) * (right-left)ans = max(ans, area)if height[left] < height[right]:left += 1else:right -= 1return ans

1.2 快慢指针

快慢指针指的是两个指针从同一侧开始遍历序列,且移动的步长一个快一个慢。移动快的指针被称为 「快指针(fast)」,移动慢的指针被称为「慢指针(slow)」。两个指针以不同速度、不同策略移动,直到快指针移动到数组尾端,或者两指针相交,或者满足其他特殊条件时为止。

例题1

  • 题目

    删除有序数组中的重复项。

  • 要求

    删除数组 nums 中的重复元素,使每个元素只出现一次。并输出去除重复元素之后数组的长度。

  • 思路

    定义两个快慢指针 slow,fast。其中 slow 指向去除重复元素后的数组的末尾位置。fast 指向当前元素。令 slow 在后, fast 在前。令 slow = 0,fast = 1。比较 slow 位置上元素值和 fast 位置上元素值是否相等。如果不相等,则将 slow 后移一位,将 fast 指向位置的元素复制到 slow 位置上。将 fast 右移 1 位。

  • 代码:

class Solution:def removeDuplicates(self, nums: List[int]) -> int:if len(nums) <= 1:return len(nums)slow, fast = 0, 1while (fast < len(nums)):if nums[slow] != nums[fast]:slow += 1nums[slow] = nums[fast]fast += 1return slow + 1

1.3 分离双指针

分离双指针:两个指针分别属于不同的数组 / 链表,两个指针分别在两个数组 / 链表中移动。

例题1

  • 题目

    两个数组的交集。

  • 要求

    编写一个函数来计算它们的交集。重复元素只计算一次。

  • 思路

    对数组 nums1、nums2 先排序。使用两个指针 left_1、left_2。left_1 指向第一个数组的第一个元素,即:left_1 = 0,left_2 指向第二个数组的第一个元素,即:left_2 = 0。如果 nums1[left_1] 等于 nums2[left_2],则将其加入答案数组(注意去重),并将 left_1 和 left_2 右移。

  • 代码:

class Solution:def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:nums1.sort()nums2.sort()left_1 = 0left_2 = 0res = []while left_1 < len(nums1) and left_2 < len(nums2):if nums1[left_1] == nums2[left_2]:if nums1[left_1] not in res:res.append(nums1[left_1])left_1 += 1left_2 += 1elif nums1[left_1] < nums2[left_2]:left_1 += 1elif nums1[left_1] > nums2[left_2]:left_2 += 1return res

2. 滑动窗口简介

滑动窗口(Sliding Window):在给定数组 / 字符串上维护一个固定长度或不定长度的窗口。可以对窗口进行滑动操作、缩放操作,以及维护最优解操作。

2.1 固定长度的滑动窗口

固定长度的滑动窗口的窗宽是不变的。

例题1

  • 题目

    大小为 K 且平均值大于等于阈值的子数组数目。

  • 要求

    返回长度为 k 且平均值大于等于 threshold 的子数组数目。

  • 思路

    ans 用来维护答案数目。window_sum 用来维护窗口中元素的和。
    left 、right 都指向序列的第一个元素,即:left = 0,right = 0。当窗口元素个数不足 k 个时,不断移动 right,先将 k 个元素填入窗口中。当窗口元素个数为 k 时,即:right - left + 1 >= k 时,判断窗口内的元素和平均值是否大于等于阈值。如果满足,则答案数目 + 1。然后向右移动 left,从而缩小窗口长度,即 left += 1,使得窗口大小始终保持为 k。向右移动 right,将元素填入窗口中。重复 3 ~ 5 步,直到 right 到达数组末尾。

  • 代码:
class Solution:def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int:left = 0right = 0window_sum = 0ans = 0while right < len(arr):window_sum += arr[right]if right - left + 1 >= k:if window_sum >= k * threshold:ans += 1window_sum -= arr[left]left += 1right += 1return ans

2.2 不定长度的滑动窗口

不定长度的滑动窗口的窗宽是变动的。

例题1

  • 题目

    无重复字符的最长子串 。

  • 要求

    找出其中不含有重复字符的 最长子串 的长度。

  • 思路

    一开始,left、right 都指向 0。将最右侧字符 s[right] 加入当前窗口 window 中,记录该字符个数。如果该窗口中该字符的个数多于 1 个,即 window[s[right]] > 1,则不断右移 left,缩小滑动窗口长度,并更新窗口中对应字符的个数,直到 window[s[right]] <= 1。维护更新无重复字符的最长子串长度。然后右移 right,直到 right >= len(nums) 结束。输出无重复字符的最长子串长度。

  • 代码:
class Solution:def lengthOfLongestSubstring(self, s: str) -> int:left = 0right = 0window = dict()ans = 0while right < len(s):if s[right] not in window:window[s[right]] = 1else:window[s[right]] += 1while window[s[right]] > 1:window[s[left]] -= 1left += 1ans = max(ans, right - left + 1)right += 1return ans

例题2

  • 题目

    长度最小的子数组。

  • 要求

    给定一个只包含正整数的数组 nums 和一个正整数 target。找出数组中满足和大于等于 target 的长度最小的「连续子数组」,并返回其长度。如果不存在符合条件的子数组,返回 0。

  • 思路

    一开始,left、right 都指向 0。向右移动 right,将最右侧元素加入当前窗口和 window_sum 中。如果 window_sum >= target,则不断右移 left,缩小滑动窗口长度,并更新窗口和的最小值,直到 window_sum < target。然后继续右移 right,直到 right >= len(nums) 结束。输出窗口和的最小值作为答案。

  • 代码:
class Solution:def minSubArrayLen(self, target: int, nums: List[int]) -> int:size = len(nums)ans = size + 1left = 0right = 0window_sum = 0while right < size:window_sum += nums[right]while window_sum >= target:ans = min(ans, right - left + 1)window_sum -= nums[left]left += 1right += 1return ans if ans != size + 1 else 0

例题3

  • 题目

    乘积小于K的子数组。

  • 要求

    给定一个正整数数组 nums和整数 k 。找出该数组内乘积小于 k 的连续的子数组的个数。

  • 思路

    一开始,left、right 都指向 0。将最右侧元素加入当前子数组乘积 window_product 中。如果 window_product >= k ,则不断右移 left,缩小滑动窗口长度,并更新当前乘积值 window_product 直到 window_product < k。记录累积答案个数 += 1,向右移动 right,直到 right >= len(nums) 结束。输出累积答案个数。

  • 代码:
class Solution:def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:if k <= 1:return 0size = len(nums)left = 0right = 0window_product = 1count = 0while right < size:window_product *= nums[right]while window_product >= k:window_product /= nums[left]left += 1count += (right - left + 1)right += 1return count

数组双指针和数组窗口相关推荐

  1. leetcode练习一:数组(二分查找、双指针、滑动窗口)

    文章目录 一. 数组理论基础 二. 二分查找 2.1 解题思路 2.2 练习题 2.2.1 二分查找(题704) 2.2.2 搜索插入位置(题35) 2.2.3 查找排序数组元素起止位置(题34) 2 ...

  2. 双指针解决数组排序问题

    这个问题如果注意,用一句就可以解决 sort(nums.begin(),nums.end()); 完事.但是人家明确说了,不能用代码库中的排序函数.我们就得自己去实现排序.其实这个问题很简单,因为里面 ...

  3. 数组双指针之快慢指针

    数组双指针 数组双指针分为两类: 左右双指针:左指针在最左侧,右指针在最右侧,它们相向而行 快慢双指针:在单链表中经常使用fast.slow指针去判断链表中是否成环.单链表的终点.单链表倒数第k个结点 ...

  4. 经典算法:双指针问题--数组合并

    算法-程序的灵魂,没错就是灵魂 ! 今天我们来聊聊关于双指针问题中的数组合并问题 内容参考:<你也能看得懂的Python算法书> 转载请标注: https://blog.csdn.net/ ...

  5. strcmp可以比较数组么_数组:总结篇

    给「代码随想录」一个星标吧! ❝ 周末我们做个总结吧 ❞ 数组理论基础 数组是非常基础的数据结构,在面试中,考察数组的题目一般在思维上都不难,主要是考察对代码的掌控能力 也就是说,想法很简单,但实现起 ...

  6. matlab单元数组和结构,Matlab使用单元数组和结构数组

    Matlab使用字符串数组.单元数组(cell array)和结构数组 (struct array) 要在MALTAB中实现比较复杂的编程,就不能不用单元数组(cell array)和结构数组(str ...

  7. 数组(有序数组)的公共部分

    https://blog.csdn.net/napoay/article/details/79195162 1. 双指针 如果数组无序,可先进行排序: 仅统计次数: def array_interse ...

  8. php 三维数组合并成二维数组_Excel VBA 数组知识点,数组能不能用好,就看这个你学会没有...

    今天和大家要说的是VBA数组的应用,上篇文章我们说的是数组的基础知识点,有不明白的小伙伴可以点击链接进行知识点回顾,今天主要说说数组的几个操作方法,这个是我们以后会经常遇到的,也是经常会使用的方法. ...

  9. matlab中定义文本数组,MATLAB字符串数组的创建与运算

    MATLAB字符串数组的创建与运算 字符串数组主要用于可视化编程内容,如界面设计和图形绘制. 1.字符串变量的创建 字符变量的创建方法是:在指令窗口中先把待建的字符放在"单引号对" ...

最新文章

  1. 【c语言】C语言配置文件解析库——iniparser
  2. 基于NPOI的Excel数据导入
  3. 获取浏览器窗口宽高问题总结
  4. 复习webpack4之实现简易的webpack
  5. NeurIPS 2019最热趋势-贝叶斯深度学习
  6. linux——线程通信(1)
  7. dj鲜生-27-登陆装饰器-使用django内置的登陆装饰器
  8. idea+selenium代码_你试过使用selenium爬虫抓取数据吗
  9. UNIX环境高级编程——线程同步之条件变量以及属性
  10. Unity教程之-Unity Attribute的使用总结
  11. Linux定时任务cron及部分Linux命令
  12. linux驱动开发:mma7660 sensor的配置
  13. 喜马拉雅FM下载的音频文件保存在哪_怎么导出来
  14. AE剪辑快捷键有哪些?这波快捷键分享拿好了
  15. 西数打造面向数据中心的Gold产品组合
  16. 基于人脸面部检测的口罩识别系统
  17. 计算机专业里的麦课尔雅,艺术导论超星尔雅网课答案2020年_高校邦_计算机文化基础_章节答案...
  18. php+mysql 留言板系统 学生适用
  19. npm installl preinstall
  20. 转载:十句触动心灵的诗句

热门文章

  1. webrequest 访问https url代
  2. 量化策略篇:股票多头策略、CTA策略、期权策略
  3. Visio取消显示两直线之间的跨线
  4. pythoncad二次开发视频_pycad学习笔记(一)
  5. pyside2 系列之介绍,安装,简单例子
  6. 评估软件项目的经济价值
  7. 十大经典三维动画制作软件
  8. Linux-C C语言编译过程
  9. sip协议呼叫流程详解
  10. 网站如何防止DDOS攻击?