代码随想录算法训练营第七天|454.四数相加II,383. 赎金信,15. 三数之和,18. 四数之和,总结

力扣题部分:

454.四数相加II

题目链接:. - 力扣(LeetCode)  ​​​​​

思路(map哈希表):

        将数组分为两组分别用双重for循环遍历。第一组将来自不同数组的两个数之和(记为sum1)作为map的key,两个数之和出现的次数作为map的value,第二组通过在map查询来自不同数组的两个数之和(记为sum2)的相反数,确定是否存在sum1 = - sum2(也就是符合题意的sum1+sum2 = 0)如果找到了,将初值为0的计数器count += map[sum1],count加第一组map的value是因为这个sum2能和第一组中不同的sum1组合,有多少出现次数就能组合成多少不同组合。

代码实现:

383. 赎金信

题目链接:. - 力扣(LeetCode)

思路(数组哈希表):

        由于ransomNote和magazine只由小写字母组成,很容易想到数组哈希表,先将magazine存放进数组,记录各个字母出现频率,然后再读取ransomNote,读一个字母数组对应值减一,一旦ransomNote小于零,就说明ransomNote中的字母有比magazine多的,换句话说ransomNote不能由magazine里面的字符构成,return false。如果读完没有小于零的,说明符合题意,return true。

代码实现:        

15. 三数之和

题目链接:. - 力扣(LeetCode)

思路(哈希表):

        虽然可以用哈希表解决本题,但是不是很推荐(效率不高)

        这道题和上一道有很大不同的一点是去重。题目要求的是不能出现三个数值一模一样的数组(调换顺序也不行)有的人一开始就想着把符合条件的三元组放进vector中,然后再去重。然而这样是非常费时的,很容易超时,也是这道题目通过率如此之低的根源所在。由于去重的过程不好处理,有很多小细节,如果在面试中很难想到位。

        用哈希表的话很容易就能知道代码需要两层for循环,最后一个数用哈希表查找。但是由于不能采取先放进结果(result数组)里再对结果去重的方法,我们就需要排序用位置解决去重问题,在数据放进去之前就保证不重复,三个数的去重得分开进行,利用排序后一样值的位置是连续的这一特点,将经过相邻的值相同的元素在合适的时机continue保证结果不重复。所以要注意的去重细节是编写代码最困难的地方。然而即便如此,哈希表相比双指针法速度仍然略逊一筹。

(”合适的时机“在本题两个代码实现后面会有详细解释)

代码实现:

思路(双指针法):

        还是两层一样的for循环,不同的是第二层的指针left要和第三个指针right进行不一样的移动操作:通过nums[left] + nums[right] 与 0 - nums[i] 对比,前者大了的话 right--就往里走使值变小,小了的话就让left++使值变大,两个操作不断将值逼近达到 a + b + c = 0 (也就是nums[left] + nums[right] + nums[i]) = 0。当然我们仍然需要在合适的时机continue保证结果不重复。

 代码实现:

 关于“合适的时机”

        理解这个其实才是对整个题目代码是否ac的关键,涉及到去重的逻辑思考。

        说到去重,其实主要考虑三个数的去重。a, b ,c, 对应的就是 nums[i],nums[left],nums[right](哈希表思路同理)。对于a来说 去重的操作是if(i > 0 && nums[i] == nums[i - 1])

        下面思考一下这个问题: 把原操作换成 if(nums[i] == nums[i + 1])行不行呢?

        答案是不行,就比如遍历[-1,-1,2]时,换掉的操作会直接continue 错过这个正确答案。

        这体现了我们的一个去重的原则:

        我们要做的是 不能有重复的三元组,但三元组内的元素是可以重复的!

        对b,c去重的双指针法中还有一个要注意的点:

         如果把双指针法的代码中第18 - 20行换成如下格式,是否有必要呢?

while (right > left) {
    if (nums[i] + nums[left] + nums[right] > 0) {
        right--;
        // 去重 right
        while (left < right && nums[right] == nums[right + 1]) right--;
    } else if (nums[i] + nums[left] + nums[right] < 0) {
        left++;
        // 去重 left
        while (left < right && nums[left] == nums[left - 1]) left++;
    } else {
    }
}

        答案是否定的,这种去重其实对提升程序运行效率没有帮助。

        拿right去重为例,即使不加这个去重逻辑,依然根据 while (right > left) 和 if (nums[i] + nums[left] + nums[right] > 0) 去完成right-- 的操作。

        多加了 while (left < right && nums[right] == nums[right + 1]) right--; 这一行代码,其实就是把 需要执行的逻辑提前执行了,但并没有减少 判断的逻辑。

        最直白的思考过程,就是right还是一个数一个数的减下去的,所以在哪里减的都是一样的。

        所以这种去重 是可以不加的。 仅仅是 把去重的逻辑提前了而已。

18. 四数之和

题目链接:. - 力扣(LeetCode)

思路(双指针法):

基本和三数之和差不多,就是向外套一层for循环,但是题目有一些变化导致需要注意更多的点。

比如target 不是 0 导致如果target 是负数,nums[k] > target 就不能是直接返回的条件了,例如[-4,-3,-2,-1],target = -10。如果照搬就直接返回错过正确答案了。所以要在原来的基础上加条件, 变成 if(nums[k] > target && (nums[i] >=0 || target >=0)) 才能达到剪枝的效果。

代码实现:

总结:

        通过这两天对哈希表的学习,我掌握了哈希表的使用方法和依据题目使用哪一种哈希表结构的挑选思路,我可以明显的感受到今天的题目难度比之前的难得多,需要思考的细节也有很多。当然,题目难度的上升能使我接触更多的解决方法,学会挑选方法思考方法优劣可以说是我学这哈希表这一章最大的收获了。
 

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值