
教程文档【免费下载链接】LogicStack-LeetCode公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码项目地址https://gitcode.com/gh_mirrors/lo/LogicStack-LeetCode点击查看免费下载本文是「宫水三叶的刷题日记」系列仓库见 README.md针对 LeetCode 第 2824 题「统计和小于目标的下标对数目」的完整技术指南。这道难度为「简单」的题目同时考察排序、二分查找与双指针三大基础算法是理解「先排序、再借助单调性消除一维枚举」这一经典优化思路的最佳入门题之一。读完本文你将掌握两种可复现、可提交的解法排序 二分、排序 双指针并理解二者复杂度差异及适用边界还能通过仓库中的姊妹题 611. 有效三角形的个数中等 看到同一套思路在中等难度题目上的延伸应用。题目描述给你一个下标从0开始长度为n的整数数组nums和一个整数target请你返回满足0 i j n且nums[i] nums[j] target的下标对(i, j)的数目。示例 1输入nums [-1,1,2,3,1], target 2 输出3 解释总共有 3 个下标对满足题目描述 - (0, 1) 0 1 且 nums[0] nums[1] 0 target - (0, 2) 0 2 且 nums[0] nums[2] 1 target - (0, 4) 0 4 且 nums[0] nums[4] 0 target 注意 (0, 3) 不计入答案因为 nums[0] nums[3] 不是严格小于 target 。示例 2输入nums [-6,2,5,-2,-7,-1,3], target -2 输出10 解释总共有 10 个下标对满足题目描述 - (0, 1) 0 1 且 nums[0] nums[1] -4 target - (0, 3) 0 3 且 nums[0] nums[3] -8 target - (0, 4) 0 4 且 nums[0] nums[4] -13 target - (0, 5) 0 5 且 nums[0] nums[5] -7 target - (0, 6) 0 6 且 nums[0] nums[6] -3 target - (1, 4) 1 4 且 nums[1] nums[4] -5 target - (3, 4) 3 4 且 nums[3] nums[4] -9 target - (3, 5) 3 5 且 nums[3] nums[5] -3 target - (4, 5) 4 5 且 nums[4] nums[5] -8 target - (4, 6) 4 6 且 nums[4] nums[6] -4 target提示1 nums.length n 50-50 nums[i], target 50本题原题与官方题解可参见 LeetCode 原题页外部站点仅供查阅题目原文。题解正文见仓库文件 2824. 统计和小于目标的下标对数目简单。基本分析排序是破题的关键注意到n 50数据范围非常小即使直接暴力枚举所有(i, j)下标对也只有 $O(n^2) 2500$ 量级的检查完全可行。但作为算法训练本题的核心价值在于展示「排序 单调性」这一通用优化范式。为了方便先对nums进行排序。排序之后数组拥有了有序特性剩下的问题就变成「遍历右端点在右端点左侧找最大合法左端点」或「遍历左端点在左端点右侧找最大合法右端点」。这两种视角分别对应下文的两套解法。需要强调的一点是排序不会改变问题的答案。题目只统计满足nums[i] nums[j] target的下标对数目排序只是打乱了元素的下标但元素之间的配对关系与数量完全不变因此对排序后的数组做统计结果与原数组完全一致。从单调性上看排序后数组满足若nums[j] nums[i] target则所有下标小于j的元素nums[k] (k j)与nums[i]的和必然也小于target因为nums[k] nums[j]。正是这条性质使得我们不需要逐一枚举每个候选左端点而可以一次性「成段」地统计合法个数——这就是两种解法都能做到比暴力更优的理论基础。解法一排序 二分这是一种「遍历右端点在右端点左侧找最大合法左端点」的做法。遍历右端点i然后在[0, i - 1]范围内进行二分找到最大的满足nums[j] nums[i] target的位置j。若存在这样的左端点j说明以nums[i]为右端点时共有j 1个范围为[0, j]合法左端点需要被统计进答案。这里的关键细节是二分模板的选择由于我们要找的是满足条件的最大下标即「右边界」应使用l mid 1型模板的镜像版本即mid l r 1 1向上取整并在满足nums[mid] nums[i] target时收缩左边界l mid否则收缩右边界r mid - 1循环结束后需要再校验一次nums[r] nums[i] target因为可能存在整个[0, i - 1]区间都不满足条件的情况此时不能累加任何计数。Java 代码class Solution { public int countPairs(ListInteger nums, int target) { Collections.sort(nums); int n nums.size(), ans 0; for (int i 1; i n; i) { int l 0, r i - 1; while (l r) { int mid l r 1 1; if (nums.get(mid) nums.get(i) target) l mid; else r mid - 1; } if (nums.get(r) nums.get(i) target) ans r 1; } return ans; } }C 代码class Solution { public: int countPairs(vectorint nums, int target) { sort(nums.begin(), nums.end()); int n nums.size(), ans 0; for (int i 1; i n; i) { int l 0, r i - 1; while (l r) { int mid l r 1 1; if (nums[mid] nums[i] target) l mid; else r mid - 1; } if (nums[r] nums[i] target) ans r 1; } return ans; } };Python 代码class Solution: def countPairs(self, nums: List[int], target: int) - int: nums.sort() n, ans len(nums), 0 for i in range(1, n): l, r 0, i - 1 while l r: mid l r 1 1 if nums[mid] nums[i] target: l mid else: r mid - 1 if nums[r] nums[i] target: ans r 1 return ansTypeScript 代码function countPairs(nums: number[], target: number): number { nums.sort((a,b)a-b); let n nums.length, ans 0; for (let i 1; i n; i) { let l 0, r i - 1; while (l r) { const mid l r 1 1; if (nums[mid] nums[i] target) l mid; else r mid - 1; } if (nums[r] nums[i] target) ans r 1; } return ans; };复杂度分析时间复杂度排序复杂度为 $O(n\log{n})$构造答案复杂度为 $O(n\log{n})$每个右端点一次二分。整体复杂度为 $O(n\log{n})$。空间复杂度$O(\log{n})$排序所需的栈空间未使用额外数组。解法二排序 双指针这是一种「遍历左端点在左端点右侧找最大合法右端点」的做法。使用l和r分别指向排序好的nums的首尾。若当前nums[l] nums[r] target说明此时对于l来说r并不合法对r自减左移。直到满足nums[l] nums[r] target此时对于l来说找到了最右侧的合法右端点r在[l 1, r]期间的数必然仍满足nums[l] nums[r] target因为数组有序nums[l] nums[k] nums[l] nums[r] target对任意k r成立共有r - l个范围为[l 1, r]合法右端点需要被统计。统计完毕后将l右移一位进入下一轮。这个做法的精妙之处在于r指针全程只减不增。当l增大时nums[l]变大此前收缩下来的r只会继续收缩而不会回退因此整个双指针扫描是线性的 $O(n)$。Java 代码class Solution { public int countPairs(ListInteger nums, int target) { Collections.sort(nums); int n nums.size(), ans 0; for (int l 0, r n - 1; l r; l) { while (r 0 nums.get(l) nums.get(r) target) r--; if (l r) ans r - l; } return ans; } }C 代码class Solution { public: int countPairs(vectorint nums, int target) { sort(nums.begin(), nums.end()); int n nums.size(), ans 0; for (int l 0, r n - 1; l r; l) { while (r 0 nums[l] nums[r] target) r--; if (l r) ans r - l; } return ans; } };Python 代码class Solution: def countPairs(self, nums: List[int], target: int) - int: nums.sort() n, ans len(nums), 0 l, r 0, n - 1 while l r: while r 0 and nums[l] nums[r] target: r - 1 if l r: ans r - l l 1 return ansTypeScript 代码function countPairs(nums: number[], target: number): number { nums.sort((a,b)a-b); let n nums.length, ans 0; for (let l 0, r n - 1; l r; l) { while (r 0 nums[l] nums[r] target) r--; if (l r) ans r - l; } return ans; };复杂度分析时间复杂度排序复杂度为 $O(n\log{n})$构造答案复杂度为 $O(n)$双指针各扫描一遍。整体复杂度为 $O(n\log{n})$。空间复杂度$O(\log{n})$。两种解法对比与易错点维度排序 二分排序 双指针统计视角遍历右端点i在[0, i-1]中二分找最大合法左端点遍历左端点l收缩右指针r找最大合法右端点构造答案复杂度$O(n\log{n})$$O(n)$整体复杂度$O(n\log{n})$$O(n\log{n})$瓶颈在排序实现要点右边界二分模板mid l r 1 1循环后需二次校验r只减不增统计量为r - l几个值得注意的易错点严格小于题目要求nums[i] nums[j] target是严格小于。二分判断与双指针收缩时都要用 target target的情况必须排除对应示例 1 中(0, 3)不计数。二分模板的选择解法一找的是「满足条件的最大下标」必须使用右边界模板mid向上取整若误用mid l r 1的下取整模板会在只有两个元素时陷入死循环或漏统计。循环后的二次校验解法一中若整个[0, i-1]区间都不满足条件例如nums[0] nums[i] targetwhile循环结束后r指向的位置可能并不合法必须先校验再累加r 1否则会把不合法的一对计入答案。双指针的边界解法二中while (r 0 ...)的r 0保护是必要的——当左指针较小时右指针可能一路收缩到-1此时l r的判断会自然跳过统计保证安全。仓库视角本题在「刷穿 LeetCode」系列中的位置本题已收录于「宫水三叶的刷题日记」系列仓库README.md是该系列的第No.2824篇。在本仓库中本题同时出现在三个算法专题索引中方便按 Tag 检索排序与611. 有效三角形的个数、2300. 咒语和药水的成功对数等同属于「先排序再利用单调性」的经典问题二分本题对应「右边界二分」模板与704. 二分查找、35. 搜索插入位置等构成二分模板的完整拼图双指针本题对应「相向双指针 单调收缩」范式与11. 盛最多水的容器、15. 三数之和等共享同一思维模型。延伸同一思路在「有效三角形的个数」中的应用本题与仓库中的 611. 有效三角形的个数中等 是同门姊妹题二者 Tag 完全一致排序、二分、双指针且解法骨架几乎一一对应。在 611 题中需要统计满足nums[k] nums[j] nums[i]的三元组个数。分析思路是排序后「先枚举较大数在下标不超过较大数下标范围内找次大数在下标不超过次大数下标范围内找较小数」从而把三维枚举降为「两层枚举 一层二分」或「一层枚举 双指针」排序 二分枚举较大数下标i和次大数下标j后在[0, j)范围内二分找满足nums[mid] nums[j] nums[i]的最小合法下标k此时用的是左边界模板mid l r 1则[k, j)范围内的j - k个元素全部合法整体复杂度 $O(n^2\log{n})$排序 双指针枚举较大数下标i用j从i-1向左递减、k从0向右递增while (k j nums[k] nums[j] nums[i]) k;收缩后累加j - k整体复杂度 $O(n^2)$。对比两道题可以清晰地看到二分模板选择的规律找「最大合法下标」用右边界模板mid上取整找「最小合法下标」用左边界模板mid下取整。把 2824 与 611 放在一起练习就能把「排序 二分 / 双指针」这一对组合拳彻底吃透。小结核心结论一排序是前提。排序后利用单调性合法左/右端点必然形成连续区间可以成段统计避免逐个枚举。核心结论二两种解法的实现复杂度。排序 二分构造答案 $O(n\log{n})$排序 双指针构造答案 $O(n)$整体均受排序瓶颈限制为 $O(n\log{n})$但双指针写法更简洁、常数更小是本题推荐的首选写法。核心结论三二分模板的正确选用。找最大合法位置用右边界模板l mid分支 mid上取整且循环结束后必须二次校验这两个细节是二分解法通过与否的关键。扩展价值本题是「排序 二分/双指针」入门题掌握后可进一步挑战 611. 有效三角形的个数中等二元组变三元组、15. 三数之和中等增加去重约束等进阶题。赞分享教程文档【免费下载链接】LogicStack-LeetCode公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码项目地址https://gitcode.com/gh_mirrors/lo/LogicStack-LeetCode点击查看免费下载相关推荐LogicStack-LeetCode 刷穿系列LeetCode 16. 最接近的三数之和排序 双指针精讲LogicStack LeetCode 刷穿系列LeetCode 16. 最接近的三数之和排序 双指针精讲 本文是 LogicStack LeetCo教程文档LogicStack-LeetCode 题解精讲LeetCode 15. 三数之和排序 双指针LogicStack LeetCode 题解精讲LeetCode 15. 三数之和排序 双指针 本指南以「宫水三叶的刷题日记」刷穿 LeetCode教程文档LogicStack-LeetCode 刷穿 LeetCode1877 数组中最大数对和的最小值贪心 排序双指针全解LogicStack LeetCode 刷穿 LeetCode1877 数组中最大数对和的最小值贪心 排序双指针全解 本篇技术指南基于 LogicSt教程文档上一篇ansible-collection-hardening 之 mysql_hardening 角色演进实录从 1.0.0 到 2.2.2 的 MySQL 加固能力迭代剖析下一篇CANN PTO-ISA TROWARGMAX 指令完全指南行归约 argmax 语义、布局约束与 tmp 临时 tile 规划创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考