当前位置: 首页 > news >正文

LeetCode 18.四数之和

1.思路:和hot100 15.三数之和一样,先排序。排序后,枚举nums[i]作为第一个数,枚举nums[j]作为第二个数,那么问题就变成找到另外两个数,使得这四个数的和等于target,这可以用双指针解决

2.优化思路:

对于nums[i]的枚举:

(1)设s = nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3]。由于数组已经排序,如果s > target,后面无论怎么选,选出的四个数之和都不会比s还小,所以后面不会找到等于target的四数之和了。所以只要s > target,就可以直接break外层循环了。

(2)设s = nums[i] + nums[n - 1] + nums[n - 2] + nums[n - 3]。由于数组已经排序,如果s < target,nums[i]加上后面的任意三个数都不会超过s了,所以无法在后面找到另外三个数与nums[i]相加等于target但是nums[i]可以继续往后找更大的nums[i],还是可能会出现四数之和等于target的情况的,所以还需要继续枚举,continue外层循环

(3)如果i > 0且nums[i] = nums[i - 1],那么nums[i]和后面数字相加的结果,必然在之前算出过,所以无需执行后续代码,直接continue外层循环(可以放在循环开头判断)

对于nums[j]的枚举(j从i + 1开始),也同样有类似的优化:

(1)设s = nums[i] + nums[j] + nums[j + 1] + nums[j + 2]。由于数组已经排序,如果s > target,后面无论怎么选,选出的四个数之和都不会比s还小,所以后面不会找到等于target的四数之和了。所以只要s > target,就可以直接break了。

(2)设s = nums[i] + nums[j] + nums[n - 1] + nums[n - 2]。由于数组已经排序,如果s < target,nums[i]加上nums[j]再加上后面任意两个数都不会超过s了,所以无法在后面找到另外两个数与nums[i]和nums[j]相加等于target但是nums[j]可以继续往后找更大的nums[j],还是可能会出现四数之和等于target的情况的,所以还需要继续枚举,continue

(3)如果j > i + 1且nums[j] = nums[j - 1],那么nums[j]和后面数字相加的结果,必然在之前算出过,所以无需执行后续代码,直接continue(可以放在循环开头判断)。注意这里的j > i + 1的判断是必须的,如果不判断,对于示例2这样的数据,会直接continue,漏掉符合要求的答案。

3.复杂度分析:

(1)时间复杂度:O(n^3),其中n为nums的长度。排序O(nlogn)。两重循环枚举第一个数和第二个数,然后O(n)双指针枚举第三个数和第四个数。所以总的时间复杂度为O(n^3)。

(2)空间复杂度:O(1)。忽略返回值和排序的栈开销,仅用到若干变量。

附代码:

class Solution { public List<List<Integer>> fourSum(int[] nums, int target) { Arrays.sort(nums); List<List<Integer>> res = new ArrayList<>(); int n = nums.length; for(int i = 0;i < n - 3;i++){ // 枚举第1个数 // 使用long避免溢出 long x = nums[i]; if(i > 0 && x == nums[i - 1]){ // 跳过重复数字 continue; } // 这里四数之和有可能是long范围 // 由于x设置为了long类型,数组元素在参与运算时也会自动升级为long运算,避免了溢出 if(x + nums[i + 1] + nums[i + 2] + nums[i + 3] > target){ // 优化1 break; } // 这里四数之和有可能是long范围 // 由于x设置为了long类型,数组元素在参与运算时也会自动升级为long运算,避免了溢出 if(x + nums[n - 3] + nums[n - 2] + nums[n - 1] < target){ // 优化2 continue; } for(int j = i + 1;j < n - 2;j++){ // 枚举第2个数 // 使用long避免溢出 long y = nums[j]; if(j > i + 1 && y == nums[j - 1]){ // 跳过重复数字 continue; } // 这里四数之和有可能是long范围 // 由于x和y设置为了long类型,数组元素在参与运算时也会自动升级为long运算,避免了溢出 if(x + y + nums[j + 1] + nums[j + 2] > target){ // 优化1 break; } // 这里四数之和有可能是long范围 // 由于x和y设置为了long类型,数组元素在参与运算时也会自动升级为long运算,避免了溢出 if(x + y + nums[n - 2] + nums[n - 1] < target){ // 优化2 continue; } int k = j + 1; int l = n - 1; // 双指针枚举第3个数和第4个数 while(k < l){ long sum = x + y + nums[k] + nums[l]; if(sum > target){ l--; }else if(sum < target){ k++; }else{ // 四数之和等于target // 由于x和y定义为了long类型,但res定义为了List<Integer>类型。而非List<Long>类型 // x和y为long型会导致所有参数都升级为long类型 // 因此需要先强制把x和y转换为int类型 res.add(List.of((int) x,(int) y,nums[k],nums[l])); k++; l--; // 数组已排序,相同的数字会相邻,需跳过重复数字 // 跳过重复数字 while(k < l && nums[k] == nums[k - 1]){ k++; } // 跳过重复数字 while(k < l && nums[l] == nums[l + 1]){ l--; } } } } } return res; } }

ACM模式:

import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Scanner; class Solution { public List<List<Integer>> fourSum(int[] nums, int target) { Arrays.sort(nums); List<List<Integer>> res = new ArrayList<>(); int n = nums.length; for (int i = 0; i < n - 3; i++) { long x = nums[i]; if (i > 0 && x == nums[i - 1]) { continue; } if (x + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) { break; } if (x + nums[n - 3] + nums[n - 2] + nums[n - 1] < target) { continue; } for (int j = i + 1; j < n - 2; j++) { long y = nums[j]; if (j > i + 1 && y == nums[j - 1]) { continue; } if (x + y + nums[j + 1] + nums[j + 2] > target) { break; } if (x + y + nums[n - 2] + nums[n - 1] < target) { continue; } int k = j + 1; int l = n - 1; while (k < l) { long sum = x + y + nums[k] + nums[l]; if (sum > target) { l--; } else if (sum < target) { k++; } else { res.add(List.of((int) x, (int) y, nums[k], nums[l])); k++; l--; while (k < l && nums[k] == nums[k - 1]) { k++; } while (k < l && nums[l] == nums[l + 1]) { l--; } } } } } return res; } } public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // 读取数组长度 int n = scanner.nextInt(); // 读取数组元素 int[] nums = new int[n]; for (int i = 0; i < n; i++) { nums[i] = scanner.nextInt(); } // 读取目标值 int target = scanner.nextInt(); // 计算四数之和 Solution solution = new Solution(); List<List<Integer>> result = solution.fourSum(nums, target); // 输出结果:每个四元组占一行,数字之间用空格分隔 for (List<Integer> list : result) { for (int j = 0; j < list.size(); j++) { System.out.print(list.get(j)); if (j < list.size() - 1) { System.out.print(" "); } } System.out.println(); } scanner.close(); } }
http://www.jsqmd.com/news/752532/

相关文章:

  • 最新媒体新闻稿发稿平台有哪些?如何选择最适合的发布渠道? - 代码非世界
  • 长期使用中感受到的 Taotoken 服务稳定性与开发者支持
  • WarcraftHelper终极指南:三步快速提升魔兽争霸III游戏体验
  • ARM调试寄存器OSLSR与OSSRR深度解析
  • Sunshine游戏串流:5步搭建你的个人云游戏服务器终极指南
  • 视频去水印免费工具怎么选?2026最新实测优缺点对比,视频去水印免费推荐 - 爱上科技热点
  • 体验快速接入如何在五分钟内让应用拥有 AI 能力
  • 5分钟快速掌握ComfyUI Manager:AI插件管理终极指南
  • GD32H759I的SRAM怎么分?手把手教你配置ITCM/DTCM提升代码性能
  • 即梦去水印方法有哪些?即梦AI图片和视频怎么去掉水印?2026最新实测教程整理 - 爱上科技热点
  • 如何用OpenDroneMap快速将无人机照片转为精准3D模型?新手完全指南
  • 即梦AI视频怎么去水印?2026最新去水印教程+工具推荐,会员无水印导出也说清了 - 爱上科技热点
  • Advanced Sessions Plugin:虚幻引擎会话管理插件终极指南
  • 具身智能(34):ROS2工具集合
  • 突破显存限制:ComfyUI-WanVideoWrapper实现1025帧长视频生成的实战指南
  • ArcGIS Pro和ArcMap数据裁剪对比:以城市绿地提取为例,我为什么推荐新工具
  • TrollInstallerX 终极安装指南:如何在 iOS 14.0-16.6.1 上轻松安装 TrollStore
  • 豆包视频怎么去水印?2026最新 豆包视频去水印方法 + 豆包视频去水印官方规定解读 - 爱上科技热点
  • 全球仅5份的《高频交易低延迟内存架构规范V2.6》中文解读(含内存池与FPGA协处理器共享零拷贝区设计细节)
  • 4399游戏平台开发技术栈拆解
  • 高效NPK文件处理工具:专业级游戏资源编辑器使用指南
  • 3步搞定AI语音转换:零基础也能玩转RVC变声神器
  • 从零开始掌握lxml.html解析:手把手教你用html.fromstring打造高效爬虫
  • 大华网络硬盘录像机dh-nvr1104hs升级
  • .NET 9容器配置实战手册(Kubernetes+Docker+Minimal Hosting三合一)
  • 别再手动备份了!用Crontab给GitLab设置每日自动备份(附Podman/宿主机两种方案)
  • 豆包视频怎么去水印?2026最新实测豆包视频官方去水印方法+工具推荐 - 爱上科技热点
  • 3步告别重复编码:obs-multi-rtmp插件实现多平台直播一次搞定
  • 终极指南:5分钟掌握NSC_BUILDER,成为Switch游戏文件管理专家
  • ThinkPHP 高并发场景下 Session 文件锁导致请求阻塞怎么优化?