双指针技巧是一种在数组、链表等线性数据结构中非常实用的算法策略。它通过使用两个指针在数据结构上按一定规则移动,来高效地解决各种问题。
双指针技巧就像是我们在处理一排物品(比如数组、链表里的元素)时,同时用两只手去操作。每只手对应一个指针,它们可以在这排物品上按照一定规则移动,通过两只手的配合来高效地完成各种任务,比如找特定的元素组合、判断是否存在某种情况等。下面详细介绍三种常见的双指针类型。
同向双指针是双指针技巧中的一种,就像是两个人在同一条道路上朝着相同的方向前进。这两个人可以代表两个指针,他们在处理数组、链表或者字符串等线性数据结构时,按照一定的规则在数据序列上移动,从而高效地解决各种问题。
同向双指针常用于处理需要在一个序列中找出满足特定条件的连续子序列、对序列进行去重或者合并等问题。比如,在一个数组中找出和为特定值的连续子数组,或者在一个有序数组中去除重复的元素。
开始时,两个指针(通常称为左指针和右指针)都位于序列的起始位置。就好像两个人站在道路的起点,准备一起出发。
右指针会先开始移动,它就像一个 “探索者”,不断地向前走,去探索序列中的元素。每移动一步,右指针会把新遇到的元素纳入到当前考虑的范围内。
在右指针移动的过程中,我们会根据具体的问题条件来判断当前由左右指针所界定的子序列是否满足要求。这就好比两个人一边走,一边检查他们走过的这段路是否符合某种标准。
如果当前子序列满足条件,我们可能会尝试让左指针也向前移动,缩小子序列的范围,看看是否还能满足条件。左指针的移动就像是后面的人向前走,尝试减少走过的路程但仍然满足标准。如果不满足条件,右指针会继续向前移动,扩大子序列的范围。
不断重复上述判断和移动指针的过程,直到右指针到达序列的末尾。
假设有一个有序数组 [1, 1, 2, 2, 2, 3, 4, 4, 5]
,我们要使用同向双指针去除其中的重复元素。
left
和右指针 right
都指向数组的第一个元素 1
。right
开始向右移动,它会依次遇到元素。当它遇到第一个 2
时,发现和左指针指向的 1
不同。left
向右移动一位,然后将右指针 right
指向的 2
放到左指针 left
现在指向的位置,也就是让数组变为 [1, 2, 2, 2, 2, 3, 4, 4, 5]
。2
,发现和左指针指向的 2
相同,右指针继续移动。当遇到 3
时,又和左指针指向的 2
不同,再次将左指针右移一位并更新数组。[1, 2, 3, 4, 5]
。假设有数组 [1, 2, 3, 4, 5]
,要找出和为 7
的连续子数组。
left
和右指针 right
都指向数组的第一个元素 1
,同时记录当前子数组的和 sum = 1
。right
向右移动一位,指向 2
,此时 sum = 1 + 2 = 3
,小于目标值 7
,右指针继续移动。3
,sum = 3 + 3 = 6
,还是小于 7
,继续移动。当右指针指向 4
时,sum = 6 + 4 = 10
,大于 7
。left
向右移动一位,指向 2
,同时更新 sum = 10 - 1 = 9
,仍然大于 7
,左指针继续移动。当左指针指向 3
时,sum = 9 - 2 = 7
,找到了满足条件的连续子数组 [3, 4]
。通过这些例子可以看出,同向双指针通过巧妙地移动左右指针,避免了很多不必要的计算,从而提高了算法的效率。
快慢双指针也是双指针技巧里很实用的一种,就好像在一个跑道上有两个运动员,一个跑得慢,一个跑得快,他们同时朝着相同方向奔跑。在算法里,这两个 “运动员” 就是两个指针,在处理数组、链表等线性数据结构时,以不同的速度移动,来解决各种各样的问题。
快慢双指针常用于链表相关问题,比如判断链表是否有环、寻找链表的中间节点、找到链表倒数第 k 个节点等。当然,在一些数组问题中也能发挥作用。
一开始,快慢两个指针都位于数据结构的起始位置。这就好比两个运动员在跑道的起点同时做好起跑准备。
快指针每次移动的 “步数” 比慢指针多。通常情况下,慢指针每次移动一步,而快指针每次移动两步。想象一下,在跑道上快的运动员一步能跨得更远,所以前进速度更快。
由于快慢指针的移动速度不同,它们之间会逐渐拉开距离,我们就利用这个速度差来达到解决问题的目的。在移动过程中,根据具体问题的要求,观察快慢指针的位置关系或者它们所指向元素的特性。
想象有一个环形跑道(代表有环的链表)和一个直线跑道(代表无环的链表)。让快慢两个运动员在跑道上跑步。
a
,等于从相遇点绕环 k - 1
圈再加上从相遇点到入环点的距离 c
。所以,当快慢指针相遇后,我们让一个指针从链表头节点开始,另一个指针从相遇点开始,两个指针都以每次移动一步的速度前进,它们最终会在入环点相遇。还是以跑道为例,假设跑道长度固定(代表链表长度固定),我们要找到跑道的中间位置。
我们可以先让快指针在跑道上提前跑 k 步,然后快慢指针再同时开始正常移动,且快指针速度还是比慢指针快。
快慢双指针通过不同的移动速度产生的速度差,巧妙地解决了很多与位置、循环相关的问题,避免了一些复杂的遍历和计算,提高了算法效率。在实际应用中,关键是要根据具体问题确定好快慢指针的初始位置、移动规则以及判断条件。
从两头往中间的双指针,也叫对撞指针,就像是两个人分别站在一条道路的两端,然后同时朝着道路中间走去。在算法里,这两个人代表两个指针,它们分别指向数据结构(如数组、字符串)的起始位置和结束位置,然后根据特定的规则向中间移动,以此来高效地解决问题。
这种双指针技巧常用于处理有序的数据结构,例如有序数组或有序链表。常见的应用场景包括:
将一个指针(左指针)指向数据结构的起始位置,另一个指针(右指针)指向数据结构的结束位置。就像两个人站在道路的两端做好出发准备。
根据具体问题的条件,移动左右指针。通常有以下几种情况:
当左右指针相遇或者越过彼此时,停止操作。这就好比两个人在道路中间相遇或者已经走过了对方。
问题描述:给定一个已按照升序排列的有序数组 numbers
,找到两个数使得它们相加之和等于目标数 target
。返回这两个数的下标(下标从 1 开始)。
操作过程:
left
指向数组的第一个元素,右指针 right
指向数组的最后一个元素。sum
。
sum
等于 target
,则找到了满足条件的两个数,返回它们的下标。sum
小于 target
,说明和不够大,将左指针右移一位,让和增大。sum
大于 target
,说明和太大了,将右指针左移一位,让和减小。问题描述:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[]
的形式给出。
操作过程:
left
指向字符数组的第一个字符,右指针 right
指向字符数组的最后一个字符。相应题目链接
922. 按奇偶排序数组 II - 力扣(LeetCode)
287. 寻找重复数 - 力扣(LeetCode)
42. 接雨水 - 力扣(LeetCode)
881. 救生艇 - 力扣(LeetCode)
11. 盛最多水的容器 - 力扣(LeetCode)
475. 供暖器 - 力扣(LeetCode)
41. 缺失的第一个正数 - 力扣(LeetCode)