双指针 / 哈希表:15.三数之和 / 1.两数之和

背景

先写的是 15. 三数之和 问题,发现该题解中提及可以使用 1. 两数之和 和“双指针”方法,于是用“双指针”的方法写“两数之和”问题,但是发现此题解使用的是HashMap。于是使用哈希表去写“三数之和”问题中的twoSum,结果发现“如何规避重复情况”需要仔细斟酌。分析总结如下:

先看两个问题的要求:

三数之和:返回所有和为 0 且不重复的三元组

两数之和:返回它们的数组下标;假设每种输入只会对应一个答案

发现区别在于:

1、“三数之和”要求返回“元素”,“两数之和”要求返回“下标”。

2、(在对两道题都使用哈希表的过程中发现,当固定一个数去找另一个数时)“两数之和”只要求返回一组解,而“三数之和”要求返回所有满足的解。

为什么“两数之和”题解使用HashMap而不是双指针?

因为:“两数之和”要求返回“下标”。如果使用“双指针”,那么需要对数组排序。但是排完序后如何知道该元素在原数组中的下标呢?

一种思路:将原数组nums copy一份得到c_nums(再对原数组进行排序)。使用c_nums找到对应元素在原数组中的下标。由于最终需要返回2个下标,为了减少一次循环:

//最终return finalnums。数组finalnums初始值为{-1, -1}。
for (int i = 0; i<nums.length; i++) {
    if ((c_nums[i] == nums[start] || c_nums[i] == nums[end]) && finalnums[0] == -1) {
        finalnums[0] = i;
    } else if ((c_nums[i] == nums[start] || c_nums[i] == nums[end]) && finalnums[0] != -1) {
        finalnums[1] = i;
        break;
    }
}

而使用HashMap的话,则不需要排序(下标不变),且空间复杂度小于“再重新开辟一个相同的数组空间”。

如果“三数之和”中的twoSum使用哈希表:

因为“三数之和”需要返回所有解,所以使用哈希表找到一个解后不能break;,依然得继续找。这时,重复答案就无法避免了。而“三数之和”问题的难点就在于“如何规避重复的答案数组”。如何规避重复答案呢?对数组进行排序(双指针的前提就是排序)。

此外,HashMap中的value值原本是用来记录“该元素在数组中的下标”的,在“三数之和”问题并没有用到。考虑后换成HashSet。

如果排完序之后依然使用HashSet:

以数组{0, 0, 0, 0}为例,如何避免重复?

固定一个数,剩下的构成“两数之和”问题,所以依次固定第1个0和第2个0会产生重复答案,因此在最外层循环里加上:

if (i > 0 && nums[i] == nums[i - 1]) continue;

将剩下的元素处理成“两数之和”问题:

固定第2个0,发现第3个0可组成答案,为了避免重复,此时第4个0应该continue;,因此,在第2层循环中加上:

if (i > curi + 2 && nums[i] == nums[i - 1]) continue;

其实这里考虑欠佳,对于数组{-2, 0, 1, 1, 2},会有答案缺失。对比{0, 0, 0, 0}和{-2, 0, 1, 1, 2}会发现,第3个0是答案,所以在第4个0处需要continue;但是在数组{-2, 0, 1, 1, 2}中,第3个1不是答案,第4个1不应该continue(这里{-2, 1, 1}也恰好是一个答案)。所以当元素重复时,需要对重复元素是否构成答案进行判断,只有构成答案时,下一个元素重复时才continue。正确地,在第2层循环中加上:

//循环外:boolean flag = false;
if (i > curi + 2 && nums[i] == nums[i - 1] && flag) continue;
else if (i > curi + 2 && nums[i] != nums[i - 1]) flag = false;
...
if (hs.contains(t)) {
    flag = true;
    ...
}

总结:

1. 两数之和 用哈希表,15. 三数之和 的“twoSum”用双指针

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值