在算法设计中,双指针是一种高效优化工具,主要用于线性数据结构(如数组(数组划分和数组分块常用)、链表、字符串),通过控制两个指针的移动轨迹,将原本需要 O (n²) 时间复杂度的问题优化为 O (n),核心优势是无需额外空间(通常为 O (1) 空间复杂度) 且逻辑直观。其常见形式分为同向指针,对撞指针和快慢指针,具体解析如下:

(一) 同向指针

1. 定义与核心场景
        同向指针是双指针的另一种核心形式,指两个指针沿同一方向移动(如均从左到右),初始位置通常有先后关系(或起始于同一位置)

        核心用途是在线性结构(如数组、链表)中进行原地元素筛选、位置调整或边界划分,无需依赖数据的有序性,更侧重通过指针分工实现元素的 “有效区域” 与 “待处理区域” 分离

2. 移动逻辑
同向指针的核心是 “分工协作”:一个指针(通常称为 “慢指针”)标记 “有效元素区域” 的边界,另一个指针(通常称为 “快指针”负责遍历所有元素并筛选目标元素,两者同方向移动但节奏不同:

  • 慢指针(slow):初始位置通常为序列起点(如索引 0)或特殊标记(如 - 1),仅当快指针找到 “有效元素” 时才移动,用于记录 “已处理的有效元素” 的末尾位置;
  • 快指针(fast):从序列起点开始,始终向前移动(如每次 + 1),负责遍历所有元素,判断当前元素是否符合 “有效” 条件(如非零、非目标值等)。

简单说:快指针 “探路”,慢指针 “驻守有效区域”;快指针遇到有效元素时,慢指针 “接纳” 并前移,遇到无效元素时,快指针独自前行,慢指针保持不动。

3. 终止条件
循环终止于快指针遍历完整个序列(如快指针到达数组末尾,即 fast == 序列长度)。此时慢指针的位置通常标记了 “有效元素区域” 的结束边界(如慢指针索引 + 1 为有效元素的数量)。

4. 适用场景
同向指针的核心优势是原地处理元素重排,且能保持有效元素的相对顺序,适用于以下场景:

  • 移动特定元素:如 “移动零”(将所有 0 移到末尾,非零元素保持顺序);
  • 移除元素:如 “移除数组中等于 val 的元素”(剩余元素保持相对顺序);
  • 压缩序列:如 “删除字符串中的所有相邻重复项”“合并两个有序数组(原地)” 等。

(二) 对撞指针(左右指针)

1. 定义与核心场景
        对撞指针又称 “左右指针”其应用依赖于数据结构的有序性(如排序数组、对称字符串),核心用途是高效查找满足特定条件的元素或元素对,例如 “有序数组两数之和”“验证回文串”“反转字符串”“删除有序数组中的重复项(首尾验证版)” 等问题。
2. 移动逻辑
指针初始位置分别位于线性结构的两端,之后按照问题规则向中间同步逼近

  • 左指针(left):从序列最左端(索引 0 或起始位置)开始,每次向右侧移动,一般为++left
  • 右指针(right):从序列最右端(索引 n-1 或末尾位置)开始,每次向左侧移动,一般为--right

3. 终止条件
终止时机需结合问题目标,核心分为两类:

  • 指针相遇或错开:当无需提前匹配结果时,循环终止于left == right(两指针指向同一位置,覆盖所有元素)或left > right(两指针交叉,遍历完所有可能组合);
  • 提前匹配终止:若在移动过程中找到满足条件的结果(如nums[left] + nums[right] == target),可直接跳出循环,无需继续逼近。

(三) 快慢指针(龟兔赛跑算法)

1. 定义与核心思想
        快慢指针又称 “龟兔赛跑算法”,其核心是让两个指针在同向移动的基础上,保持不同的移动速度(如 “慢指针 1 步 / 次,快指针 2 步 / 次”)。通过速度差,可在无需额外空间的前提下,检测序列的循环特性或定位特定位置。
2. 适用场景
快慢指针的核心优势是处理 “循环相关” 或 “特定位置定位” 问题,常见场景包括:

  • 环形结构检测:如判断链表是否有环、找到环形链表的入口;
  • 特定位置定位:如查找链表的中间节点(用于归并排序)、删除链表的倒数第 k 个节点;
  • 重复元素查找:如在无额外空间的要求下,找到数组中的重复元素(如 LeetCode 287. 寻找重复数)。

3. 常见实现方式

快慢指针的速度差可根据问题灵活调整,最经典的实现是 “1:2 速度比”:

  • 慢指针(slow):每次移动 1 步(如slow = slow.next或slow += 1)
  • 快指针(fast):每次移动 2 步(如fast = fast.next.next或fast += 2)

原理类比:若序列存在环(如环形链表),快指针会像 “兔子” 一样在环内循环,最终追上慢指针(“乌龟”);若序列无环(如普通链表),快指针会率先到达序列末尾(如链表的null节点),循环自然终止。
4. 其他变种实现
除了 “1:2 速度比”,快慢指针的速度差可根据目标调整:

  • 定位链表倒数第 k 个节点:先让快指针超前慢指针 k 步,再让两者同步移动(均 1 步 / 次);当快指针到达末尾时,慢指针恰好指向倒数第 k 个节点;
  • 寻找数组中重复元素:让慢指针按 “1 步 / 次” 移动,快指针按 “nums [fast] 步 / 次” 移动(模拟循环),最终两指针会在重复元素位置相遇。

综上,双指针的选择需结合问题的数据结构特性(有序 / 无序、线性 / 环形)和目标需求(查找 / 检测 / 定位):若处理有序结构的元素查找,优先考虑对撞指针;若处理循环或特定位置问题,优先考虑快慢指针

(三) 题目

题目1:移动零

链接:283. 移动零 - 力扣(LeetCode)

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

核心思路:

该解法采用双指针(同向指针)策略,通过两个指针(dest和cur)划分数组区间,实现零元素的移动:

1.先定义两个指针——cur和dest

  • cur:从左往右扫描数组,遍历数组
  • dest:已处理的区间内,非零元素的最后一个元素

此时三个区间:

  • [0,dest] 非0区间
  • [dest+1,cur-1] 0区间
  • [cur,n-1] 待处理区间

cur遍历时:

  • 遇到0元素:cur++
  • 遇到非0元素:交换dest+1和cur元素,再dest++,cur++

小思考:

为何是交换,不是直接覆盖?

若直接覆盖,在后面还得直接加0才可以,但是你怎么知道它要加多少个0会超级麻烦,而交换就让非0元素全在左边。0元素全在右边,超级符合题目要求!

代码:

class Solution {
public:void moveZeroes(vector<int>& nums) {for(int dest = -1, cur = 0; cur < nums.size(); cur++){if(nums[cur]){swap(nums[++dest],nums[cur]);}}}
};

题目2:复写0

链接:1089. 复写零 - 力扣(LeetCode)

给你一个长度固定的整数数组 arr ,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。

注意:请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。

核心思路:

该解法采用双指针(同向指针)策略

1. 指针初始化——定义两个指针 cur 和 dest

2. 定位最后一个复写元素

结束条件:遍历数组直至 cur 或 dest 超出有效范围
遍历规则:

  • arr[cur]为0时dest走两步
  • arr[cur]部位0时dest走一步

3. 从后向前完成复写操作

  • arr[cur]为0时dest走两步并且赋值成0
  • arr[cur]部位0时dest走一步并拷贝cur的值

注意:边界处理

针对如[1, 5, 0, 0, 6, 0, 8, 9]这类可能导致 dest 越界的情况:

核心——预先处理最后一次复写

  • 将 n-1 位置设为0
  • 执行 cur-- 和 dest-=2 操作

代码:

class Solution {
public:void duplicateZeros(vector<int>& arr) {int cur = 0, dest = -1, n = arr.size();while (cur < n){if (arr[cur])dest++;elsedest += 2;if (dest >= n - 1)break;cur++;}if (dest == n){arr[n - 1] = 0;cur--;dest -= 2;}while (cur >= 0){if (arr[cur])arr[dest--] = arr[cur--];else{arr[dest--] = 0;arr[dest--] = 0;cur--;}}
}
};

题目3:快乐数

链接:202. 快乐数 - 力扣(LeetCode)

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

核心思路:

我们先来理解一下这个题目:

思路:

通过观察图片可以发现,这个问题可以类比链表的环检测,因此采用双指针(快慢指针)策略。

具体分析:

题目核心是判断数组是否存在环,且环内元素为1(即环的起始元素为1)
实际上,所有数据都会形成环。根据鸽巢原理n个鸟巢,n+1个鸽子,至少又一个鸟巢里面的鸽子数大于1)

  • 最大数为2^31 - 1 ≈ 2.1×10^9
  • 经过平方和运算后,结果范围在[1,810]之间(9^2×10=810)
  • 当任意数x运算超过811次时,必然出现重复值形成环

实现步骤:

  1. 定义快慢指针
  2. 快指针每次前进两步,慢指针每次前进一步
  3. 判断相遇时是否为1

注意事项: 虽然数字不是传统意义上的指针,但在这个问题中可以将其视为指针来操作

代码:

class Solution {
public:int bitSum(int n){int sum = 0;while(n){int a = n%10;sum += a*a;n/=10;}return sum;}bool isHappy(int n) {int slow = n, fast = bitSum(n);while(fast != slow){fast = bitSum(bitSum(fast));slow = bitSum(slow);}if(slow == 1)return true;elsereturn false;}
};

题目4:盛最多水的容器

链接:11. 盛最多水的容器 - 力扣(LeetCode)

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

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

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

核心思路:

1. 暴力解法
       设两指针i , j,分别指向⽔槽板的最左端以及最右端,此时容器的宽度为 j - i 。由于容器的⾼度由两板中的短板决定,因此可得容积公式:v = (j - i) * min( height[i], height[j] )
        但暴力解法需遍历所有可能的(i, j)组合,时间复杂度为O(n²),会超出题目时间要求,因此需要优化。
2. 优化方法:双指针(左右指针)策略(利用单调性)
采用左右指针初始分别指向数组两端(left = 0,right = n-1),通过移动指针逐步缩小范围,高效寻找最大容积。
步骤:

  1. 初始化左右指针left和right,计算当前容积并记录为初始最大值;
  2. 比较 height[left] 和 height[right] 的大小,移动高度较小的一侧指针(若相等,移动任意一侧均可);
  3. 重新计算新指针位置的容积,更新最大值;
  4. 重复2和3,直到左右指针相遇,此时记录的最大值即为结果。

移动较矮指针的原因:
假设当前height[left] < height[right](左边界为短板):容器的宽度为 right - left

  • 若移动右指针(较长的一侧):新宽度必然减小(right-1 - left < right - left),且新的有效高度仍由左边界决定(不会超过height[left]),因此容积一定会变小;
  • 若移动左指针(较短的一侧):虽然宽度减小,但可能遇到更高的左边界,新的有效高度可能增大(最多等于右边界高度),因此容积有可能变大。

同理,若height[right] < height[left],则应移动右指针。

这种策略通过放弃 “必然更小” 的可能,保留 “潜在更大” 的机会,将时间复杂度优化至O(n)。

代码:

1.暴力解法:

//盛最多水的容器暴力解法
class Solution {
public:int maxArea(vector<int>& height) {int n = height.size();int ret = 0;for (int i = 0; i < n; i++){for (int j = 0; j < n; j++){ret = max(ret, min(height[i], height[j]) * (j - i));}}return ret;}
};

2. 优化方法:双指针策略(利用单调性)


//盛最多水的容器优化解法
class Solution {
public:int maxArea(vector<int>& height) {int left = 0, right = height.size() - 1, ret = 0;while (left < right){int v = min(height[left], height[right]) * (right - left);ret = max(ret, v);if (height[left] < height[right])++left;else--right;}return ret;}
};

题目5:有效三角形的个数

链接:611. 有效三角形的个数 - 力扣(LeetCode)

给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。

核心思路:

先来想想怎么判断三个数是否构成三角形

核心条件是:任意两边之和大于第三边。由于三角形中最长边会限制另外两边的和(若最长边小于另外两边之和,则其余两边的和必然大于第三边),因此更高效的判断方式是:最小的两边之和大于最长边

代码思路:

1. 暴力解法

     先对数组排序(方便后续判断大小关系),然后枚举所有可能的三元组(i, j, k)(需满足i < j < k),通过上述条件判断是否能组成三角形。

     但这种方法需遍历所有三元组,时间复杂度为O(n³),效率较低。

2. 优化方法:双指针(左右指针)策略(利用单调性)

     核心逻辑:通过排序简化大小判断,固定最长边后,用双指针快速寻找符合条件的另外两条边,将时间复杂度降至O(n²)。

步骤:
1. 对数组排序(升序),便于利用 “最小两边之和> 最长边” 的条件;
2. 固定最长边:遍历数组,设当前最长边为nums[i](i从 2 开始,确保至少有两个更小的数);
3. 初始化双指针:

  • 左指针left = 0(指向当前范围内最小的数)
  • 右指针right = i - 1(指向当前范围内次大的数,即小于nums[i]的最大数)

4. 判断与计数:

  • 若nums[left] + nums[right] > nums[i]:由于数组递增,left到right之间的所有数(nums[left], nums[left+1], ..., nums[right-1])都大于等于nums[left],因此它们与nums[right]的和必然也大于nums[i]。即此时left到right之间的所有数与nums[right]、nums[i]均可组成三角形,共有right - left个有效三元组,计入结果后将right左移(缩小范围继续寻找)
  • 若nums[left] + nums[right] ≤ nums[i]:说明当前left对应的边太小,需右移left(增大两边之和,尝试满足条件)

5. 重复步骤4,直到left >= right,再继续固定下一个最长边i。

代码:

1. 暴力解法

//有效三角形的个数暴力解法
class Solution {
public:int triangleNumber(vector<int>& nums) {sort(nums.begin(), nums.end());int n = nums.size();int ret = 0;for (int i = 0; i < n; i++){for (int j = i + 1; j < n; j++){for (int k = j + 1; k < n; k++){if (nums[i] + nums[j] > nums[k])ret++;}}}return ret;}
};

2. 优化方法:双指针(左右指针)策略(利用单调性)

//有效三角形的个数优化解法
class Solution {
public:int triangleNumber(vector<int>& nums) {int n = nums.size() - 1;sort(nums.begin(), nums.end());int ret = 0;for (int i = n; i > 0; i--){int left = 0, right = i - 1;while (left < right){if (nums[left] + nums[right] > nums[i]){ret += (right - left);right--;}else{left++;}}}return ret;}
};

题目6:查找总价格为目标值的两个商品和为s的两个数字)

链接:LCR 179. 查找总价格为目标值的两个商品 - 力扣(LeetCode)

购物车内的商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况,返回任一结果即可。

核心思路:

1. 暴力解法

      定义两个指针i和j(i < j),遍历数组中所有可能的元素对,判断price[i] + price[j]是否等于target。若找到符合条件的 pair,直接返回{price[i], price[j]}。
      但此方法需枚举所有组合,时间复杂度为O(n²),在数据量较大时效率较低。

2. 优化方法:双指针(左右指针)策略(利用单调性)

因数组是升序排列的,可通过双指针快速缩小查找范围,将时间复杂度降至O(n)。
步骤:
1. 初始化指针:左指针left指向数组起始位置(0),右指针right指向数组末尾(n-1);
2. 循环条件:当left < right时,持续判断两指针指向元素的和;
3. 具体判断:

  • price[left] + price[right] == target:找到目标组合,直接返回{price[left], price[right]};
  • price[left] + price[right] < target:由于数组升序,right已指向当前范围内最大的元素,此时和仍小于目标值,说明price[left]过小,需增大左侧值,故将left右移(left++);
  • price[left] + price[right] > target:同理,left已指向当前范围内最小的元素,此时和仍大于目标值,说明price[right]过大,需减小右侧值,故将right左移(right--)。

代码:

1. 暴力解法

//和为s的两个数字暴力解法
class Solution {
public:vector<int> twoSum(vector<int>& price, int target) {int n = price.size();for (int i = 0; i < n; i++){for (int j = i + 1; j < n; j++){if (price[i] + price[j] == target){return{ price[i],price[j] };}}}}
};

2. 优化方法:双指针(左右指针)策略(利用单调性)

//和为s的两个数字优化解法
class Solution {
public:vector<int> twoSum(vector<int>& price, int target) {int left = 0, right = price.size() - 1;while (left < right){int sum = price[left] + price[right];if (sum == target){return { price[left], price[right] };}else if (sum > target)right--;elseleft++;}return { -1, -1 };}
};

题目7:三数之和

链接:15. 三数之和 - 力扣(LeetCode)

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

核心思路:

1. 暴力解法

      先对数组排序(为去重做准备),枚举所有可能的三元组(i, j, k),要求i < j < k,判断nums[i] + nums[j] + nums[k] == 0,若符合条件,将三元组加入结果集,同时通过跳过重复元素(相同值的i、j、k)避免重复三元组。
     但是三层循环的时间复杂度为O(n³),在数组长度较大时效率极低,无法满足时间要求。

2. 优化方法:双指针(左右指针)策略(利用单调性)

利用排序后数组的单调性,将三层循环降为两层,时间复杂度优化至O(n²),同时通过去重步骤确保结果不重复。

步骤:

1. 排序:对数组升序排序。排序的作用有二:一是使重复元素相邻,便于去重;二是为双指针移动提供单调性基础。
2. 固定第一个数:遍历数组,固定三元组的第一个数nums[i](i从 0 开始)。

  • 若nums[i] > 0,由于数组升序,后续数均大于等于nums[i],三数之和必大于 0,可直接终止循环。

3. 双指针找剩余两数:在i右侧的子数组[i+1, n-1]中,用左指针left = i+1右指针right = n-1寻找满足nums[left] + nums[right] = -nums[i]的组合:

  • 若nums[left] + nums[right] < -nums[i]:需增大两数之和,将left右移(利用升序特性,右侧数更大);
  • 若nums[left] + nums[right] > -nums[i]:需减小两数之和,将right左移;
  • 若相等:找到有效三元组,加入结果集。

4. 去重操作(关键):

  • 固定数去重:当i > 0且nums[i] == nums[i-1]时,跳过当前i(避免重复固定相同的数,导致相同三元组);
  • 双指针针去重:找到有效三元组后,需同时将left右移、right左移,并跳过所有与当前left、right相同的元素(避免同一i下出现重复三元组)。

代码:

1. 暴力解法

//三数之和暴力解法
class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> ret;sort(nums.begin(), nums.end());int n = nums.size();for (int i = 0; i < n; ){for (int j = i + 1; j < n; ){for (int k = j + 1; k < n;  ){if (nums[i] + nums[j] + nums[k] == 0)ret.push_back({ nums[i],nums[j],nums[k] });k++;while (k < n && nums[k] == nums[k - 1])k++;}j++;while (j < n && nums[j] == nums[j - 1])j++;}i++;while (i < n && nums[i] == nums[i - 1])i++;}return ret;}
};

2. 优化方法:双指针(左右指针)策略(利用单调性)

//三数之和优化解法
class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {vector<vector<int>> ret;sort(nums.begin(), nums.end());int n = nums.size();for (int i = 0; i < n; ){if (nums[i] > 0)break;int left = i + 1, right = n - 1;while (left < right){if (nums[left] + nums[right] == -nums[i]){ret.push_back({ nums[left], nums[right], nums[i] });left++;right--;while (left < right && nums[left] == nums[left-1])left++;while (left < right && nums[right] == nums[right+1])right--;}else if (nums[left] + nums[right] < -nums[i]){left++;}else {right--;}}i++;while(i < n && nums[i] == nums[i-1])i++;}return ret;}
}

题目8:四数之和

链接:18. 四数之和 - 力扣(LeetCode)

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abc 和 d 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

核心思路:

1. 暴力解法

       先对数组排序(为去重做准备);枚举所有可能的四元组(i, j, k, z),要求i < j < k < z,判断nums[i] + nums[j] + nums[k] + nums[z] == target;若符合条件,将四元组加入结果集,同时通过跳过重复元素(相同值的a、b、c、d)避免重复四元组。

       但是四层循环的时间复杂度为O(n⁴),在数组长度较大时运算量极大,无法满足时间要求。

2. 优化方法:双指针(左右指针)策略(利用单调性)

利用排序后的单调性,通过 “固定两个数 + 双指针找剩余两个数” 的方式,将四层循环降为三层,时间复杂度优化至O(n³),同时通过多层去重确保结果唯一。

步骤:

1. 排序预处理:对数组升序排序。排序的作用:一是使重复元素相邻,便于后续去重;二是为双指针移动提供单调性基础,减少无效判断
2. 固定前两个数:

  • 第一层循环固定第一个数nums[i](i从 0 开始),若i > 0且nums[i] == nums[i-1],则跳过当前i(去重,避免重复四元组);
  • 第二层循环固定第二个数nums[j](j从i+1开始),若j > i+1且nums[j] == nums[j-1],则跳过当前j(进一步去重)。

3. 双指针找剩余两数:在j右侧的子数组[j+1, n-1]中,用左指针left = j+1右指针right = n-1寻找满足nums[left] + nums[right] == target - nums[i] - nums[j]的组合:

  • 若nums[left] + nums[right] < 目标剩余和(即target - nums[i] - nums[j]):需增大两数之和,将left右移(利用升序特性,右侧数更大);
  • 若nums[left] + nums[right] > 目标剩余和:需减小两数之和,将right左移;
  • 若nums[left] + nums[right] == 目标剩余和:找到有效四元组,加入结果集,随后同时将left右移、right左移,并跳过所有与当前left、right相同的元素(双指针去重,避免同一i、j下出现重复四元组)。

代码:

1. 暴力解法

1//四数之和暴力解法
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> ret;sort(nums.begin(), nums.end());int n = nums.size();for (int i = 0; i < n; i++){for (int j = i + 1; j < n; j++){for (int k = j + 1; k < n; k++){for (int z = k + 1; z < n; z++){if (nums[i] + nums[j] + nums[k] + nums[z] == target){ret.push_back({ nums[i],nums[j],nums[k],nums[z] });}}}}}return ret;}
};

2. 优化方法:双指针(左右指针)策略(利用单调性)

//四数之和优化解法
class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {vector<vector<int>> v;sort(nums.begin(), nums.end());int n = nums.size();for (int i = 0; i < n; ){for (int j = i + 1; j < n; ){int left = j + 1, right = n - 1;long long aim = (long long)target - nums[i] - nums[j];while (left < right){if (nums[left] + nums[right] == aim){v.push_back({ nums[i], nums[j], nums[left], nums[right] });left++;right--;while (left < right && nums[left] == nums[left - 1])left++;while (left < right && nums[right] == nums[right + 1])right--;}else if (nums[left] + nums[right] < aim)left++;elseright--;}j++;while (j < n && nums[j] == nums[j - 1])j++;}i++;while (i < n && nums[i] == nums[i - 1])i++;}return v;}
};

博主的错误小记录(这个是博主把这里当作直接的笔记了哈哈哈哈哈哈)

LeetCode出现超出时间是循环出现了问题!!!可以去调试!!!


以上就是算法之双指针的学习就到这里告一段落,后续的完善工作我们将留待日后进行。希望这些知识能为你带来帮助!如果觉得内容实用,欢迎点赞支持~ 若发现任何问题或有改进建议,也请随时与我交流。感谢你的阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/98409.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/98409.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/98409.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

幂等性、顺序性保障以及消息积压

幂等性 概念 在应用程序中&#xff0c;幂等性就是指对一个系统进行重复调用&#xff08;相同参数&#xff09;&#xff0c;不论请求多少次&#xff0c;这些请求对系统的影响都是相同的效果. 比如数据库的select操作.不同时间两次查询的结果可能不同&#xff0c;但是这个操作…

算法训练营DAY58 第十一章:图论part08

拓扑排序精讲 卡码网&#xff1a;117. 软件构建(opens new window) 题目描述&#xff1a; 某个大型软件项目的构建系统拥有 N 个文件&#xff0c;文件编号从 0 到 N - 1&#xff0c;在这些文件中&#xff0c;某些文件依赖于其他文件的内容&#xff0c;这意味着如果文件 A 依…

如何在Python中使用正则表达式?

在Python中使用正则表达式主要通过内置的re模块实现。正则表达式用于匹配、查找、替换字符串中的特定模式&#xff0c;是处理文本的强大工具。以下是使用正则表达式的核心方法和示例&#xff1a; 一、基本用法步骤 导入re模块&#xff1a;import re定义正则表达式模式&#xff…

用 Trae 玩转 Bright Data MCP 集成

引言 在自动化与智能体浪潮中&#xff0c;Trae 以“开箱即用、所见即所得”的工具编排体验&#xff0c;成为个人与团队落地 AI 工作流的高效选择。本篇将以 Trae 为主角&#xff0c;展示如何通过最少配置完成与 Bright Data MCP 的对接&#xff0c;并快速构建一个可用、可观测…

大数据Spark(六十三):RDD-Resilient Distributed Dataset

文章目录 RDD-Resilient Distributed Dataset 一、RDD五大特性 二、RDD创建方式 RDD-Resilient Distributed Dataset 在 Apache Spark 编程中&#xff0c;RDD&#xff08;Resilient Distributed Dataset&#xff0c;弹性分布式数据集&#xff09;是 Spark Core 中最基本的数…

java,通过SqlSessionFactory实现动态表明的插入和查询(适用于一个版本一个表的场景)

1,测试实体类package org.springblade.sample.test;import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data;/*** Author: 肖扬* CreateTime: 2025-09-05* Description: SqlSessionFactoryTest测试* Version: 1.0*/ Data TableName("session_factory_…

鹧鸪云光储流程系统全新升级:视频指引与分阶段模块使用指南

鹧鸪云光储流程系统近日完成重要更新&#xff0c;全面优化了操作指引体系&#xff0c;为用户带来更高效、直观的使用体验。本次升级重点推出了全套功能操作视频&#xff0c;并明确了不同业务阶段的核心模块使用指南&#xff0c;助力用户快速上手、提升工作效率。全覆盖视频操作…

ChatGPT 协作调优:把 SQL 查询从 5s 优化到 300ms 的全过程

ChatGPT 协作调优&#xff1a;把 SQL 查询从 5s 优化到 300ms 的全过程 &#x1f31f; Hello&#xff0c;我是摘星&#xff01; &#x1f308; 在彩虹般绚烂的技术栈中&#xff0c;我是那个永不停歇的色彩收集者。 &#x1f98b; 每一个优化都是我培育的花朵&#xff0c;每一个…

复杂计算任务的智能轮询优化实战

目录 复杂计算任务的智能轮询优化实战 一、轮询方法介绍 二、三种轮询优化策略 1、用 setTimeout 替代 setInterval 2、轮询时间指数退避 3、标签页可见性检测&#xff08;Page Visibility API&#xff09; 三、封装一个简单易用的智能轮询方法 四、结语 作者&#xff…

Java开发中常用CollectionUtils方式,以及Spring中CollectionUtils常用方法示例

场景 Java开发中常用的CollectionUtils 一、Spring Framework的CollectionUtils 包路径&#xff1a;org.springframework.util.CollectionUtils 核心方法&#xff1a; isEmpty(Collection<?> coll) List<String> list null; boolean empty CollectionUtil…

人工智能学习:Transformer结构(文本嵌入及其位置编码器)

一、输入部分介绍 输入部分包含: 编码器源文本嵌入层及其位置编码器 解码器目标文本嵌入层及其位置编码器 在transformer的encoder和decoder的输入层中,使用了Positional Encoding,使得最终的输入满足: 这里,input_embedding是通过常规embedding层,将每一个词的…

⸢ 肆 ⸥ ⤳ 默认安全建设方案:c-1.增量风险管控

&#x1f44d;点「赞」&#x1f4cc;收「藏」&#x1f440;关「注」&#x1f4ac;评「论」 在金融科技深度融合的背景下&#xff0c;信息安全已从单纯的技术攻防扩展至架构、合规、流程与创新的系统工程。作为一名从业十多年的老兵&#xff0c;将系统阐述数字银行安全体系的建设…

第二课、熟悉Cocos Creator 编辑器界面

本文主要介绍Cocos Creator 编辑器界面中几个常规的面板功能&#xff0c;让新手了解编辑器界面中常规的面板功能&#xff0c;更好的使用Cocos Creator 编辑器。一、编辑器界面常规面板划分Cocos Creater编辑器默认样式如上&#xff0c;主要包含&#xff1a;1、工具栏&#xff0…

Elixir通过Onvif协议控制IP摄像机,扩展ExOnvif的摄像头连续移动功能 ContinuousMove

Elixir 通过Onvif 对IP设备进行控制时&#xff0c;可以使用 ExOnvif 库。ExOnvif官方文档 此文章仅提供了ContinuousMove的控制方式及示例。 Elixir Onvif协议控制IP设备的其他命令&#xff0c;可以参考以下链接 绝对移动 【AbsoluteMove】 调用指定预置位 【GotoPreset】 …

android studio JNI 环境配置实现 java 调用 c/c++

1、在 app 级的 build.gradle 文件配置两个地方 android{ defaultConfig{ // 在 defaultConfig 里配置下面代码 externalNativeBuild { cmake { cppFlags "-frtti -fexceptions"//添加对 c 的异常处理支持 …

静态时序分析详解之时序路径类型

目录 一、概览 二、时序路径 2.1 数据路径 2.2 时钟路径 2.3 时钟门控路径 2.4 异步路径 2.5 关键路径 2.6 False路径 2.7 单周期路径 2.8 多周期路径 2.9 最长路径和最短路径 三、参考资料 一、概览 ​ ​静态时序分析通过模拟最差条件下分析所有的时序路径&am…

SpringBoot埋点功能技术实现方案深度解析:架构设计、性能优化与扩展性实践

SpringBoot埋点功能技术实现方案深度解析&#xff1a;架构设计、性能优化与扩展性实践 1. 原理剖析与技术实现细节 1.1 埋点技术基本原理 埋点&#xff08;Tracking&#xff09;是通过在代码中植入特定逻辑&#xff0c;收集用户行为数据、系统运行状态和业务指标的技术手段。在…

自建prometheus监控腾讯云k8s集群

自建prometheus监控腾讯云k8s集群 使用场景 k8s集群&#xff08;腾讯云容器服务&#xff09; promtheus (外部自建服务) 腾讯云提供了容器内部自建 Prometheus 监控 TKE 集群的文档&#xff0c;参考。 当前的环境promethues建在k8S外的云服务器上&#xff0c;与上面链接文…

2025高教社国赛数学建模C题参考论文(含模型和代码)

2025 年高教社杯大学生数学建模竞赛 C 题参考论文 目录 NIPT 的时点选择与胎儿的异常判定 摘要 1 问题重述 2 问题分析 2.1 问题 1 分析 2.2 问题 2 分析 2.3 问题 3 分析 2.4 问题 4 分析 3 模型假设与符号定义 3.1 模型假设 4. 孕周在 10-25 周内检测有…

iOS开发环境搭建及打包流程

一、下载xcode 直接去苹果商店的appstore下载就行 二、clone项目 1.登录xcode苹果账号或对应代码仓库账号 2.clone项目 3.安装设备真机环境&#xff08;未安装过的话&#xff09; 三.安装cocoapods 1. 检查并更新 Ruby 环境 CocoaPods 是基于 Ruby 编写的&#xff0c;因此…