问题简介
编程思路
- 三层循环-暴力方法必须掌握
- 双向遍历
代码实现
第一个版本
ThreeNum类
import java.util.Objects;
/**
* 三数之和等于0
*
* @author songquanheng
* 2022/2/10-8:52
*/
public class ThreeNum {
private Integer a;
private Integer b;
private Integer c;
public ThreeNum(Integer a, Integer b, Integer c) {
this.a = a;
this.b = b;
this.c = c;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ThreeNum threeNum = (ThreeNum) o;
return Objects.equals(a, threeNum.a) &&
Objects.equals(b, threeNum.b) &&
Objects.equals(c, threeNum.c);
}
@Override
public int hashCode() {
return Objects.hash(a, b, c);
}
public Integer getA() {
return a;
}
public void setA(Integer a) {
this.a = a;
}
public Integer getB() {
return b;
}
public void setB(Integer b) {
this.b = b;
}
public Integer getC() {
return c;
}
public void setC(Integer c) {
this.c = c;
}
}
主体方法
public List<List<Integer>> threeSum(int[] nums) {
Set<ThreeNum> result = new HashSet<>();
for(int i=0; i<nums.length; i++) {
for(int j=i+1;j<nums.length; j++) {
for (int k=j+1; k<nums.length; k++) {
if (nums[i] + nums[j] + nums[k] != 0) {
continue;
}
List<Integer> list = new ArrayList<>(Arrays.asList(nums[i], nums[j], nums[k]));
Collections.sort(list);
result.add(new ThreeNum(list.get(0), list.get(1), list.get(2)));
}
}
}
List<List<Integer>> res = new ArrayList<>();
for (ThreeNum threeNum : result) {
List<Integer> temp = new ArrayList<>();
temp.addAll(Arrays.asList(threeNum.getA(), threeNum.getB(), threeNum.getC()));
res.add(temp);
}
return res;
}
通过了315/318个测试用例,说明算法主题思想是正确的。
第二个版本
在构造过程中进行了排序,我们首先对于nums数组进行排序。
ThreeNum类没有变化。
主体方法
public List<List<Integer>> threeSum(int[] nums) {
List<Integer> param = Arrays.stream(nums)
.boxed().sorted().collect(Collectors.toList());
Set<ThreeNum> result = new HashSet<>();
int size = param.size();
for(int i = 0; i< size; i++) {
for(int j=i+1;j<size; j++) {
for (int k=j+1; k<size; k++) {
Integer first = param.get(i);
Integer second = param.get(j);
Integer third = param.get(k);
if (first + second + third != 0) {
continue;
}
result.add(new ThreeNum(first, second, third));
}
}
}
List<List<Integer>> res = new ArrayList<>();
for (ThreeNum threeNum : result) {
List<Integer> temp = new ArrayList<>();
temp.addAll(Arrays.asList(threeNum.getA(), threeNum.getB(), threeNum.getC()));
res.add(temp);
}
return res;
}
转化为二层循环-实质上还是三层循环
public List<List<Integer>> threeSum(int[] nums) {
List<Integer> param = Arrays.stream(nums)
.boxed().sorted().collect(Collectors.toList());
Set<ThreeNum> result = new HashSet<>();
int size = param.size();
for (int i = 0; i < size; i++) {
for (int j = i + 1; j < size; j++) {
Integer first = param.get(i);
Integer second = param.get(j);
Integer thirdShould = 0 - first - second;
if (param.lastIndexOf(thirdShould) > j) {
result.add(new ThreeNum(first, second, thirdShould));
} else {
continue;
}
}
}
List<List<Integer>> res = new ArrayList<>();
for (ThreeNum threeNum : result) {
List<Integer> temp = new ArrayList<>();
temp.addAll(Arrays.asList(threeNum.getA(), threeNum.getB(), threeNum.getC()));
res.add(temp);
}
return res;
}
第三个版本
主体方法
public List<List<Integer>> threeSum(int[] nums) {
List<Integer> param = Arrays.stream(nums)
.boxed().sorted().collect(Collectors.toList());
Set<ThreeNum> result = new HashSet<>();
int size = param.size();
for (int i = 0; i < size; i++) {
Integer first = param.get(i);
for (int j = i + 1; j < size; j++) {
Integer second = param.get(j);
Integer thirdShould = 0 - first - second;
for (int k=j+1; k<size; k++) {
if (param.get(k)>thirdShould) {
break;
}
if (param.get(k).equals(thirdShould)) {
result.add(new ThreeNum(first, second, thirdShould));
}
}
}
}
List<List<Integer>> res = new ArrayList<>();
for (ThreeNum threeNum : result) {
List<Integer> temp = new ArrayList<>();
temp.addAll(Arrays.asList(threeNum.getA(), threeNum.getB(), threeNum.getC()));
res.add(temp);
}
return res;
}
参考别人思想-左右移动
到此,笔者已经黔驴技穷了,只好参考别人的思路了。
第一个版本
说到底这个思想与盛最多的水是异曲同工的事情。
public List<List<Integer>> threeSum(int[] nums) {
List<Integer> param = Arrays.stream(nums)
.boxed().sorted().collect(Collectors.toList());
Set<ThreeNum> result = new HashSet<>();
int size = param.size();
for (int i = 0; i < size; i++) {
Integer first = param.get(i);
int j = i + 1;
int k = size - 1;
while (j < k) {
Integer second = param.get(j);
Integer third = param.get(k);
int sum = first + second + third;
if (sum > 0) {
k--;
} else if (sum < 0) {
j++;
} else {
result.add(new ThreeNum(first, second, third));
break;
}
}
}
List<List<Integer>> res = new ArrayList<>();
for (ThreeNum threeNum : result) {
List<Integer> temp = new ArrayList<>();
temp.addAll(Arrays.asList(threeNum.getA(), threeNum.getB(), threeNum.getC()));
res.add(temp);
}
return res;
}
如果直接break,则只会找到一组解
[[-2, 0, 2]]
而
[-2, 1, 1]会丢失掉
所以,不是找到一个就返回,而是找到一组解之后,继续移动指针。
所以
主体方法-AC版本
public List<List<Integer>> threeSum(int[] nums) {
List<Integer> param = Arrays.stream(nums)
.boxed().sorted().collect(Collectors.toList());
Set<ThreeNum> result = new HashSet<>();
int size = param.size();
for (int i = 0; i < size; i++) {
Integer first = param.get(i);
int j = i + 1;
int k = size - 1;
while (j < k) {
Integer second = param.get(j);
Integer third = param.get(k);
int sum = first + second + third;
if (sum > 0) {
k--;
} else if (sum < 0) {
j++;
} else {
result.add(new ThreeNum(first, second, third));
j++;
k--;
}
}
}
List<List<Integer>> res = new ArrayList<>();
for (ThreeNum threeNum : result) {
List<Integer> temp = new ArrayList<>();
temp.addAll(Arrays.asList(threeNum.getA(), threeNum.getB(), threeNum.getC()));
res.add(temp);
}
return res;
}
优化
引入去重移除ThreeNum类
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
return result;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.length - 1;
while (right > left) {
int sum = nums[i] + nums[left] + nums[right];
if (sum > 0) {
right--;
} else if (sum < 0) {
left++;
} else {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 这个地方是关键
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
right--;
left++;
}
}
}
return result;
}
}
总结
昨天和父母聊天,聊的很晚,自己以后要扩充心凉心眼小是藏不住的。自己一定要把心整的很大。海纳百川有容乃大,一定不要心里装太多不满的事情,要看开放下,达观处世。