欢迎光临
我们一直在努力

LeetCode100 15. 三数之和

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

这里想到的第一个方案是 两数之和再加一个数

(其实暴力 n3 最好想,但是 leetcode 还是直接 pass 吧)

遍历一个数 另一个当两数之和来写

class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
Set<List<Integer>> resSet = new HashSet<>();
int n = nums.length;
for (int i = 0; i < n; i++) {
if (i > 0 && nums[i] == nums[i-1]) continue;
Map<Integer, Integer> map = new HashMap<>();
for (int j = i + 1; j < n; j++) {
int target = -(nums[i] + nums[j]);
if (map.containsKey(target)) {
resSet.add(Arrays.asList(nums[i], target, nums[j]));
}
map.put(nums[j], j);
}
}
return new ArrayList<>(resSet);
}
}

虽然过了,但是超过了 5.01% 人 好不优雅啊

看了眼题解 排序这步对了, 但是更好的方法是双指针

还是先排序,然后外层 i 遍历数组

内层 l 和 r 指向 i + 1 和 n – 1

如果nums[i] + nums[l] + nums[r] = 0 则记录三元组

如果小于零 i 是不动的 r 是最大的 l++

如果大于零 l 是最小的 r–

这样两个指针就会不断维护一个和 sums【i】相加最接近零的一个窗口

class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
int n = nums.length;

// 外层循环:固定第一个数 a = nums[i]
for(int i = 0; i < n; i++){
// 剪枝:数组升序,a>0 后面都是更大的正数,三数之和不可能为0
if(nums[i] > 0) break;
// a去重:和上一轮a相同,会产生重复答案,跳过
if(i > 0 && nums[i] == nums[i-1]) continue;

// l 左指针(b)从i下一位开始,r右指针(c)在数组末尾
int l = i + 1, r = n – 1;
// 在区间 [l, r] 内双指针查找 b + c = -nums[i]
while(l < r){
int sum = nums[i] + nums[l] + nums[r];
if(sum == 0){
// 找到一组符合条件的三元组,加入结果
res.add(Arrays.asList(nums[i], nums[l], nums[r]));
// 先把指针移动到下一个位置
l++;
r–;
// b去重:新的l不能等于刚才选过的nums[l-1]
while(l < r && nums[l] == nums[l-1]) l++;
// c去重:新的r不能等于刚才选过的nums[r+1]
while(l < r && nums[r] == nums[r+1]) r–;
}else if(sum < 0){
// 和偏小,需要更大的值,左指针右移
l++;
}else{
// 和偏大,需要更小的值,右指针左移
r–;
}
}
}
return res;
}
}

这里还有一个关键就是去重

其实这里 可以 set 去重省事一点 但是空间上不占优

abc 都要去重,去重的逻辑是每个都和前一各比是否重 只要前面有了 就过

(这里要明白为什么不和后一个比,这里用测试用例走一遍就能看出来,影响正常判断了 -1 -1 1)

还有一个剪枝优化 数组后面都是整数 所以 i 没必要遍历到整数直接退出循环就行

赞(0)
未经允许不得转载:171主机测评 » LeetCode100 15. 三数之和
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址