乘风破浪:LeetCode真题_015_3Sum

一、前言

关于集合的操作,也是编程最容易考试的问题,比如求集和中的3个元素使得它们的和为0,并且要求不重复出现,这样的问题该怎么样解决呢?

二、3Sum

2.1 问题

2.2 分析与解决

    遇到这样的问题,我们更应该仔细想想如果使用暴力法的时间效率,可以发现是O(n~3)的时间复杂度,并且会出现重复过的,需要去除重复,这是非常让人难以忍受的,并且我们知道随着集合的增大,这样的代价非常的大。那么我们如何进行优化呢,这里就要仔细的想想如果我们在第一个循环中确定了前面的元素,后面的循环不能再使用这些元素,并且在后面的操作中找到两个数,这样两个数的和正好等于负的当前的数,这样和就为零了。但是我们这样做还是会有很多的问题,比如无法去除重复的情况,因此,如果我们实现对数组进行排序,得到从小到大的数组,然后在进行上面的操作,如果遇到相邻的两个数是相等的,我们就跳过去,这样就完美的解决问题了。

比如下面的代码:

class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> out = new ArrayList<>();
if (nums == null || nums.length < 3) {
return out;
}
Arrays.sort(nums); for (int i = 0; i < nums.length-2; i++) {
//NO duplicates allowed!
if (i > 0 && nums[i] == nums[i-1]) {
continue;
}

int complement = -1*nums[i];
int j = i+1;
int k = nums.length - 1;
//no way for a triplet to be equal to 0.
if (nums[j] > complement) {
continue;
}
while(j < k) {
int sum = nums[j] + nums[k];
if(sum == complement) {
out.add(Arrays.asList(new Integer[]{nums[i],nums[j],nums[k]}));
j++;
k--;
while(j < k && nums[j] == nums[j-1]) {
j++;
}
while(j < k && nums[k] == nums[k+1]) {
k--;
}
} else if (sum < complement) {
j++;
} else {
k--;
}

}
}
return out;
}
}

      这样的时间复杂度,排序以及后面算法一共为O(n~2)。

同时还可以做一些改进,将下面代码进行优化,

                } else if (sum < complement) {
j++;
} else {
k--;
}

      直接寻找到最适合的目标。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List; public class Solution {
/**
* Note:
* The solution set must not contain duplicate triplets.
*
* For example, given array S = {-1 0 1 2 -1 -4},
* A solution set is:
* (-1, 0, 1)
* (-1, -1, 2)
*
* 题目大意:
* 给定一个n个元素的数组,是否存在a,b,c三个元素,使用得a+b+c=0,找出所有符合这个条件的三元组
*
* 注意:
* - 三元组中的元素必须是非递减的
* - 结果不能包含重复元素
*
* 解题思路:
* 可以在 2sum问题 的基础上来解决3sum问题,假设3sum问题的目标是target。每次从数组中选出一个数k,
* 从剩下的数中求目标等于target-k的2sum问题。这里需要注意的是有个小的trick:当我们从数组中选出
* 第i数时,我们只需要求数值中从第i+1个到最后一个范围内字数组的2sum问题。
*
* 我们以选第一个和第二个举例,假设数组为A[],总共有n个元素A1,A2....An。很显然,当选出A1时,
* 我们在子数组[A2~An]中求目标位target-A1的2sum问题,我们要证明的是当选出A2时,我们只需要在
* 子数组[A3~An]中计算目标位target-A2的2sum问题,而不是在子数组[A1,A3~An]中,证明如下:
* 假设在子数组[A1,A3~An]目标位target-A2的2sum问题中,存在A1 + m = target-A2(m为A3~An中
* 的某个数),即A2 + m = target-A1,这刚好是“对于子数组[A3~An],目标位target-A1的2sum问题”
* 的一个解。即我们相当于对满足3sum的三个数A1+A2+m = target重复计算了。因此为了避免重复计算,
* 在子数组[A1,A3~An]中,可以把A1去掉,再来计算目标是target-A2的2sum问题。
*
* 对于本题要求的求最接近解,只需要保存当前解以及当前解和目标的距离,如果新的解更接近,则更新解。
* 算法复杂度为O(n^2);
*/
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new LinkedList<>(); if (nums != null && nums.length > 2) {
// 先对数组进行排序
Arrays.sort(nums);
// i表示假设取第i个数作为结果
for (int i = 0; i < nums.length - 2; ) {
// 第二个数可能的起始位置
int j = i + 1;
// 第三个数可能是结束位置
int k = nums.length - 1; while (j < k) {
// 如果找到满足条件的解
if (nums[j] + nums[k] == -nums[i]) {
// 将结果添加到结果含集中
List<Integer> list = new ArrayList<>(3);
list.add(nums[i]);
list.add(nums[j]);
list.add(nums[k]);
result.add(list); // 移动到下一个位置,找下一组解
k--;
j++; // 从左向右找第一个与之前处理的数不同的数的下标
while (j < k && nums[j] == nums[j - 1]) {
j++;
}
// 从右向左找第一个与之前处理的数不同的数的下标
while (j < k && nums[k] == nums[k + 1]) {
k--;
}
}
// 和大于0
else if (nums[j] + nums[k] > -nums[i]) {
k--;
// 从右向左找第一个与之前处理的数不同的数的下标
while (j < k && nums[k] == nums[k + 1]) {
k--;
}
}
// 和小于0
else {
j++;
// 从左向右找第一个与之前处理的数不同的数的下标
while (j < k && nums[j] == nums[j - 1]) {
j++;
}
}

} // 指向下一个要处理的数
i++;
// 从左向右找第一个与之前处理的数不同的数的下标
while (i < nums.length - 2 && nums[i] == nums[i - 1]) {
i++;
}
}
} return result;
}
}

三、总结

看到了这样的问题,我们将三个数的求和转换成了两个数的求和,这是非常具有开创性的意见的,同时对于更多的数,我们也可以转换成三个数,最后转成两个数的求和算法来计算,另外对于一些优化问题,也是值得我们去思考的。

乘风破浪:LeetCode真题_015_3Sum的更多相关文章

  1. 乘风破浪:LeetCode真题_041_First Missing Positive

    乘风破浪:LeetCode真题_041_First Missing Positive 一.前言 这次的题目之所以说是难,其实还是在于对于某些空间和时间的限制. 二.First Missing Posi ...

  2. 乘风破浪:LeetCode真题_040_Combination Sum II

    乘风破浪:LeetCode真题_040_Combination Sum II 一.前言 这次和上次的区别是元素不能重复使用了,这也简单,每一次去掉使用过的元素即可. 二.Combination Sum ...

  3. 乘风破浪:LeetCode真题_039_Combination Sum

    乘风破浪:LeetCode真题_039_Combination Sum 一.前言     这一道题又是集合上面的问题,可以重复使用数字,来求得几个数之和等于目标. 二.Combination Sum ...

  4. 乘风破浪:LeetCode真题_038_Count and Say

    乘风破浪:LeetCode真题_038_Count and Say 一.前言     这一道题目,很类似于小学的问题,但是如果硬是要将输入和结果产生数值上的联系就会产生混乱了,因此我们要打破思维定势. ...

  5. 乘风破浪:LeetCode真题_037_Sudoku Solver

    乘风破浪:LeetCode真题_037_Sudoku Solver 一.前言 这次我们对于上次的模型做一个扩展并求解. 二.Sudoku Solver 2.1 问题 2.2 分析与解决     这道题 ...

  6. 乘风破浪:LeetCode真题_036_Valid Sudoku

    乘风破浪:LeetCode真题_036_Valid Sudoku 一.前言 有的时候对于一些基础知识的掌握,对我们是至关重要的,比如ASCII重要字符的表示,比如一些基本类型的长度. 二.Valid ...

  7. 乘风破浪:LeetCode真题_035_Search Insert Position

    乘风破浪:LeetCode真题_035_Search Insert Position 一.前言 这次的问题比较简单,也没有限制时间复杂度,但是要注意一些细节上的问题. 二.Search Insert ...

  8. 乘风破浪:LeetCode真题_034_Find First and Last Position of Element in Sorted Array

    乘风破浪:LeetCode真题_034_Find First and Last Position of Element in Sorted Array 一.前言 这次我们还是要改造二分搜索,但是想法却 ...

  9. 乘风破浪:LeetCode真题_033_Search in Rotated Sorted Array

    乘风破浪:LeetCode真题_033_Search in Rotated Sorted Array 一.前言     将传统的问题进行一些稍微的变形,这个时候我们可能无所适从了,因此还是实践出真知, ...

随机推荐

  1. MYSQL的NOW和SYSDATE函数的区别

    在MySQL Performance Blog博客上看到一篇文章介绍now()和sysdate()函数. 想起很多朋友专门问在MySQL里面提供now()和sysdate()函数,都是表示取得当前时间 ...

  2. POJ 3481 Double Queue(set实现)

    Double Queue The new founded Balkan Investment Group Bank (BIG-Bank) opened a new office in Buchares ...

  3. PHP的一些语句 if...else...elseif - Switch - while - for

    条件语句用于基于不同条件执行不同的动作 PHP 条件语句 在您编写代码时,经常会希望为不同的决定执行不同的动作.您可以在代码中使用条件语句来实现这一 点. 在 PHP 中,我们可以使用以下条件语句: ...

  4. php访问mysql数据库的步骤

    官方说5.5开始就废弃mysql_query()这块东西很多,当然常用的就几个还是看手册吧. 这里简单记录一下.在我失忆之后可以找回一点记忆.最近一直用框架原生的都快忘了. 1.打开mysql连接 $ ...

  5. WPF的MediaElement指定Source无法播放问题解决

    最近学wpf,在使用 MediaElement 指定 Source 进行视频播放时,在源码界面可以正常显示,但运行时控件显示空白. 源码界面如下图:(可正常显示) 运行后如下图所示:(控件位置显示空白 ...

  6. webpack工具、Vue、react模块化

    一.为什么要有webpack print('hello,world') fsdl fdsf title2 title3 引用 斜体字 加粗 有序列表1 有序列表2 无序列表1 无序列表2 行内code ...

  7. SQL Server将DataTable传入存储过程(Table Value Parameter)

    博主在做毕业设计的时候,需要用到事务处理和多次将数据写入不同的表中,但是 SQL Server 数据库是不支持数组类型变量的,想要实现数组的功能,可以通过 XML 和数据表的方法实现,但是实现方法非常 ...

  8. 解决:IDEA 中 new Java Class 怎么没了?

    前言:写代码时遇到的问题,所以记录下来.我的包名为“interface”,只有这个包及包下的文件不能建java文件. 问题 解决方式(对于普通包名) 点击应用,ok就可以了. 解决方式(对于包名为“i ...

  9. PHP获取地址栏传的id值

    function getQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&] ...

  10. angular开发中的两大问题

    一.在我们的angular开发中,会请求数据但轮播图等...在请求过数据后他的事件和方法将不再执行: 看我们的解决方案一: app.controller("text",functi ...