很有意思的一道题,值得好好思考,虽然难度只有Mid,但是个人觉得不比Hard简单

题目描述

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

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

示例 1:

输入:nums = [-1,0,1,2,-1,-4]

输出:[[-1,-1,2],[-1,0,1]]

示例 2:

输入:nums = []

输出:[]

示例 3:

输入:nums = [0]

输出:[]

提示:

  • 0 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

解答

解法一 排序+双指针+三循环

很容易想到的是先对序列进行排序,首尾两个指针,两个指针中间采取遍历,三个循环查找所有满足条件的答案。

时间复杂度为\(O(n^3)\) 空间复杂度为 \(O(n)\)

然后一开始就疯狂说超时,结果一番修改,终于不报超时了。

class Solution {

    public List<List<Integer>> threeSum(int[] nums) {
int len = nums.length;
// 对数组进行排序
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<List<Integer>>();
// 定义一个指针,指向数组的头部,不断的去遍历每一个不重复的元素
// 每次遍历中再定义一个指针指向尾部,不断往前遍历,判断是否存在合理的三元组
for (int pi = 0; pi < len - 2; pi++) {
// 很明显如果pi对应的值都大于0了,说明后面的全是大于0,已无解了
if (nums[pi] > 0) {break;}
for (int pj = len - 1; pj > pi; pj--){
// 这里的pj值小于0,也是一样的,代表这一轮已无解了直接break
if (nums[pj] < 0) {break;} for (int pz = pj - 1; pz > pi; pz--) { if (nums[pi] + nums[pj] + nums[pz] > 0) { continue; }
if (nums[pi] + nums[pj] + nums[pz] < 0) { break; } if (nums[pi] + nums[pj] + nums[pz] == 0){
List<Integer> temp = new ArrayList<Integer>();
temp.add(nums[pi]);
temp.add(nums[pz]);
temp.add(nums[pj]);
ans.add(temp);
// 找到就退出此循环
break;
}
// 走到最后一个相同元素的位置
while (pz > 0 && nums[pz] == nums[pz - 1]) {pz--;}
}
// 走到最后一个相同元素的位置
while (pj > 0 && nums[pj] == nums[pj - 1]) {pj--;}
}
// 走到最后一个相同元素的位置
while (pi < len - 1 && nums[pi] == nums[pi + 1]) {pi++;}
}
return ans;
}
} // 执行用时: 2164 ms
// 内存消耗: 42.3 MB

不报超时了,但是时间大概就比别人多了很多很多吧

果然时间复杂度太高了,但是想了半天也不知道是为什么,然后去看教程,教程说能将三循环优化成双循环。

一看代码还是三循环,人傻了反正是。

虽然没懂为什么for改成while起作用了,但是直接动手改写我最后的一层循环。

写着写着就悟了。。。果然实践出真知

直接上代码吧

解法二 排序+双指针+双循环

时间复杂度\(O(n^2)\)

class Solution {

    public List<List<Integer>> threeSum(int[] nums) {
int len = nums.length;
// 对数组进行排序
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<List<Integer>>();
// 定义一个指针,指向数组的头部,不断的去遍历每一个不重复的元素
// 每次遍历中再定义一个指针指向尾部,不断往前遍历,判断是否存在合理的三元组
for (int pi = 0; pi < len - 2; pi++) {
// pi指向的元素如果大于0,后面无解,直接返回
if (nums[pi] > 0) {return ans;}
int pz = pi + 1;
for (int pj = len - 1; pj > pi; pj--){
// pj指向的元素如果都小于0,说明剩余区域全部为零了,直接break
// pz指向大于pj时说明无解了
if (nums[pj] < 0 || pz >= pj) {break;}
// 更新pz
while (pz < pj && nums[pi] + nums[pj] + nums[pz] < 0) {
pz++;
}
// 记录pz如果合理
if (pz < pj && nums[pi] + nums[pj] + nums[pz] == 0){
List<Integer> temp = new ArrayList<Integer>();
temp.add(nums[pi]);
temp.add(nums[pz]);
temp.add(nums[pj]);
ans.add(temp);
}
// 走到最后一个相同元素的位置
while (pj > 0 && nums[pj] == nums[pj - 1]) {pj--;}
}
// 走到最后一个相同元素的位置
while (pi < len - 1 && nums[pi] == nums[pi + 1]) {pi++;}
}
return ans;
}
} // 执行用时: 24 ms
// 内存消耗: 42.8 MB

这速度反正快了很多很多吧

why?

首先说明,数组按照从小到大的顺序排序,

我们最外层的循环是遍历指向数组头的指针,Head

第二层的循环是遍历指向数组尾的指针,Tail

第三层的循环是两个首尾指针中间的区域,Mid

对于一个Head,我们的尾指针Tail 将会从最后一个元素慢慢往前遍历至头元素的前一个元素。

假如此时 Head = \(n\), Tail = \(m\),\(n<m\),如果找到一个Mid =\(x\), \(n<x<m\)

满足条件 \(nums[n] + nums[x] +nums[m] = 0\)

那么此时Tail更新为\(m-1\),那么我们第三层循环的Mid指针需要重新从\(n+1\)开始遍历吗?

答案是不需要。

为什么?

此时有 \(nums[n] + nums[x] +nums[m] = 0, nums[m-1]<nums[m]\)

所以不难推出 \(nums[n]+nums[x]+nums[m-1]<0\)

因此\(x\)左边的值都只会使不等式小于0,我们只需要从\(x\)往右开始遍历Mid就可以了。

说到这里,可以发现Mid指针,在Head指针固定的情况下,只会更新小于len次,len为数组的长度。

也就是说当Head=\(n\)时,

Mid指针只会遍历小于\(length(nums)-n\)次,

而Tail指针,也是遍历小于\(length(nums)-n\)次

分配下来,Tail的一次循环Mid更新的次数是常数项。而在某些时刻Mid会直接大于Tail,就直接退出内层循环。

因此第三层循环虽然是存在,但是在第二层循环的指针固定的情况下,只会循环常数项次,至此第三层循环由\(O(n)\)被优化至\(O(1)\)

leetcode 刷题(数组篇)15题 三数之和 (双指针)的更多相关文章

  1. LeetCode 16. 3Sum Closest(最接近的三数之和)

    LeetCode 16. 3Sum Closest(最接近的三数之和)

  2. [LeetCode] 15. 3Sum 三数之和

    Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all un ...

  3. 【LeetCode】15、三数之和为0

    题目等级:3Sum(Medium) 题目描述: Given an array nums of n integers, are there elements a, b, c in nums such t ...

  4. 【LeetCode】15. 3Sum 三数之和

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人公众号:负雪明烛 本文关键词:3sum, 三数之和,题解,leetcode, 力扣,P ...

  5. C#LeetCode刷题之#16-最接近的三数之和(3Sum Closest)

    目录 问题 示例 分析 问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3620 访问. 给定一个包括 n 个整数的 ...

  6. [leetcode]15. 3Sum三数之和

    Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find ...

  7. LeetCode第[15]题(Java):3Sum (三数之和为目标值)——Medium

    题目难度:Medium 题目: Given an array S of n integers, are there elements a, b, c in S such that a + b + c  ...

  8. Leetcode(15)-三数之和

    给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组. 注意:答案中不可以包含重复的三元组. ...

  9. 【LeetCode 15】三数之和

    题目链接 [题解] 先把n个数字升序排个序. 然后枚举三元组最左边的那个数字是第i个数字. 之后用两个指针l,r移动来获取三元组的第2个和第3个数字. (初始值,l=i+1,r = n-1); 如果a ...

  10. leecode第十六题(最接近的三数之和)

    class Solution { public: void quick_order(vector<int>& num, int star, int en)//快排 { int st ...

随机推荐

  1. element-ui的树型结构图,带有复选框的,没有子项的,横排展示

    // 修改树形图样式,如果不含有下箭头的块,要变成行内样式 treeChildInline(){ let hasCaretRight = $("#permission_panel" ...

  2. RT-Thread学习笔记3-线程间通信 & 定时器

    目录 1. 事件集的使用 1.1 事件集控制块 1.2 事件集操作 2. 邮箱的使用 2.1 邮箱控制块 2.2 邮箱的操作 3. 消息队列 3.1 消息队列控制块 3.2 消息队列的操作 4. 软件 ...

  3. NGK.IO网络安全大会暨区块链安全与应用创新论坛圆满落幕

    近日,NGK.IO网络安全大会暨区块链安全与应用创新论坛于美国McCormick Place国际会议中心圆满落幕. 论坛围绕"进化繁荣发展·安全链接未来"这一主题,由NGK.IO硅 ...

  4. 适合Linux嵌入式项目的代码构建与依赖管理工具——cazel

    前言 我们知道,现在有很多流行的优秀代码构建工具,如CMake.jetkins.bazel等.这些不同的构建工具在其应用的领域起到了举足轻重的作用. 但是,如果仔细研究就会发现,在嵌入式领域,构建工具 ...

  5. 微信小程序(七)-项目实例(原生框架 MINA转云开发)==02-云开发-配置

    云开发:1.就是用云函数的型式来使用云存储和云数据库完成各种操作!     2.只关注调什么函数,完成什么功能即可,无需关心HTTP请求哪一套!     3.此模式不代表没有服务器,只是部署在云环境中 ...

  6. Vue学习笔记-vue-element-admin 按装报错再按装

    一  使用环境 开发系统: windows 后端IDE: PyCharm 前端IDE: VSCode 数据库: msyql,navicat 编程语言: python3.7  (Windows x86- ...

  7. Pygame基础(1)

    Pygame是Python的一个很常用的游戏框架,今天我来讲一讲Pygame的基础知识 Pygame的官网:https://www.pygame.org/news Pygame的下载 打开cmd输入p ...

  8. python行与列显示不全

    在显示数据框时添加以下代码 #显示所有列 pd.set_option('display.max_columns', None) #显示所有行 pd.set_option('display.max_ro ...

  9. 算法 - 链表操作思想 && case

    算法 - 链表操作题目套路 前面这一篇文章主要讲链表操作时候的实操解决方式,本文从本质讲解链表操作的元信息,学完后,再也不怕链表操作题目了. 1.链表的基本操作 链表的基本操作无外乎插入,删除,遍历 ...

  10. 大数据开发-Spark-Streaming处理数据到mysql

    前面一篇讲到streamin读取kafka数据加工处理后写到kafka数据,大数据开发-Spark-开发Streaming处理数据 && 写入Kafka是针对比如推荐领域,实时标签等场 ...