代码随想录算法训练营第六天 哈希表part2|454.四数相加II 383. 赎金信 15. 三数之和

454.四数相加II

参考视频和详解

代码随想录

优解思路

HashMap

与Two Sum类似,用一个hashMap存储已经遍历过的a+b,把sum放进key, 把count放进value,判断c+d有没有hashMap中的元素,有的话提取map中的count,依次累加。与18.四数之和相比,不用去重

为什么用HashMap?

  • 数组 ❌ 元素数值可能很大,用数组下标做映射数值无限,所以不能用数组(对比242.valid anagram这题元素a-z 26个字母)
  • Set ❌       不仅要统计a+b是否出现,还要统计a+b出现过多少次,然后才能和c+d做映射
  • Map ✅      key:a+b, value: count

时间/空间复杂度

O(n2)

遇到的问题

1. 计数逻辑错误

  • count的初始化值是1,而不是0,因为初次加入map计数就为1,而不是0
  • 第一次for循环时,即把a+b的和放到map中时,把count当作全局变量进行++,错误地用于跟踪每个和的计数。应该每发现一个新的和,就在哈希表中初始化和更新。解决办法是把count放进for循环中
  • count放进for循环后还是有问题,这是因为每次循环对count++,而不是map.get(key)进行++,导致count的更新只会在1-2

 

2. 基本语法错误

更新map中的次数时,用map.get(sum1)++,这样时错误的,因为 map.get(sum1) 返回的是一个 Integer 对象,而不是一个原始 int 类型。在 Java 中,Integer 对象是不可变的,这意味着不能直接修改它所包含的值。因此,不能直接对从哈希表获取的值进行自增操作。

 正确的做法是:

如果 map 中已经包含了 sum1,需要先获取这个值,然后增加 1,最后再将新的值放回 map 中。

代码随想录算法训练营第六天 哈希表part2|454.四数相加II 383. 赎金信 15. 三数之和_第1张图片

或者更简洁的写法是:map.getOrDefault

代码优化

  • 增强for循环代替for循环
  • map.getOrDefault()代替if (map.containsKey(n)) else…
  • 增加map中val的计数:map.put(key, map.getOrDefault(key, 0) + 1)

代码随想录算法训练营第六天 哈希表part2|454.四数相加II 383. 赎金信 15. 三数之和_第2张图片

 

383. 赎金信

参考视频和详解

代码随想录

优解思路

数组

  1. 和anagram一样,定义一个size26的新数组。
  2. 将ransomNote和magazine转成数组
  3. 遍历ransomNote数组,对新数组元素做++
  4. 遍历magazine数组,对新数组元素做--
  5. 遍历新数组元素,有大于0的,说明false。否则返回true

时间/空间复杂度

时间复杂度:O(n+m)

空间复杂度:O(1)

遇到的问题

1. 选用什么数据结构?有没有必要用map?

        此题和anagram类似,最简单的做法也是定义一个size26的数组做哈希映射

        没有必要用map。因为hashmap的底层是红黑树,实际时间和空间耗时都要更多一些(红黑树不了解,所以这里其实不太懂

2. 如何把字符串转数组?

char[] = String.toCharArray

3. 判断逻辑。什么时候判定是false?

        ransomNote对新数组元素做自增;

        magazine对新数组元素做自减;

        如果新数组元素有大于0的,说明ransomNote中存在magazine没有的字符,返回false

4. 在自己思考的时候尝试了map方法,但是在map更新计数这里卡住

        记住map更新val的简单方法:

map.put(key, map.getOrDefault(key, 0)+1)

代码实现

代码随想录算法训练营第六天 哈希表part2|454.四数相加II 383. 赎金信 15. 三数之和_第3张图片

15. 三数之和 

参考视频和详解

代码随想录

优解思路

  • 哈希法:三元组要去重,去重细节太多(没懂
  • 双指针法:对数组进行排序,定义一个i指针,一个left指针,一个right指针。用i指针遍历,用头尾指针left和right滑动窗口调整范围
  • 本题的关键点是对a, b,c三个元素去重,因为返回的结果不能重复

时间/空间复杂度

时间复杂度:O(n2)

空间复杂度:O(1)

关键点

1. 对原数组nums进行排序

2. 对a去重

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

i++

}

不能是nums[i] == nums[i+1]。为什么?假设数组是[-1, -1, 2],此时nums[0]=nums[1]=-1,那就会漏掉[-1, -1, 2]这种可能性

3. 循环条件

while(left < right)

不能是left==right,因为right==left时,b,c都是同一个指针,重复了

4. 对b,c去重

        收获结果后继续遍历并要对b,c去重。且去重的逻辑必须放在收获结果的下面,因为至少要收获一个结果才能去重。否则会漏掉如[0,0,0,0]的结果[0,0,0]

        while(left < right && nums[left] == nums[left+1]){

                left++;

        }

*这里要加上left,因为有可能一直加到末尾,导致空指针异常

遇到的问题

1. 基本函数使用

  • 如何对array进行排序?

代码随想录算法训练营第六天 哈希表part2|454.四数相加II 383. 赎金信 15. 三数之和_第4张图片

  • 如何创建一个空数组,往里面添加新的数组?
    • 因为数组的大小在创建时就已经固定,不能直接将多个数组"添加"到一个已有的数组中。但是可以使用集合(如 ArrayList)来实现类似的功能。ArrayList 是一个动态数组,可以根据需要增长和缩减。

代码随想录算法训练营第六天 哈希表part2|454.四数相加II 383. 赎金信 15. 三数之和_第5张图片

  • 或者是:

                List<List<Integer>> arrayList = new ArrayList<>();

                arrayList.add(Arrays.asList(nums[i], nums[left], nums[right]))

 

2. 去重逻辑

 2.1. a去重:在每次循环的开始检查并跳过重复元素

❌错误(1): 把a的去重代码放在了for循环的末尾。为什么错了?

代码随想录算法训练营第六天 哈希表part2|454.四数相加II 383. 赎金信 15. 三数之和_第6张图片

❌错误(2): 对i去重的移动写的是i++,而不是continue。为什么要写continue,而不是i++,两者的区别是什么?

  • 当增加 i 的值来跳过重复元素时,应该在下一次循环的开始处再次检查是否存在重复,以避免跳过检查的情况。i++会继续执行后面的程序,会漏掉i++后对i的校验,而continue会在i++后先判断一下条件,再执行后面的程序。

代码随想录算法训练营第六天 哈希表part2|454.四数相加II 383. 赎金信 15. 三数之和_第7张图片

2.2 对b,c去重:找到有效的三元组之后去重

❌错误(1): 对b,c的去重放在了else代码块之外。这样left和right的移动会跳过一些数

❌错误(2): 对b,c的去重要用while循环来校验,而不是if,因为left和right在移动过程中可能连着好几个数字都一样

❌错误(3): 跳出去重的while循环后,没有进行left++和right--

  • 在上面的两个循环完成后,left 和 right 指针指向的是最后一个重复元素。接下来的 left++; 和 right--; 是为了将 left 和 right 分别移动到下一个新的、未检查的元素上。这样,当进入下一次大循环的时候,left 和 right 就分别指向了新的、可能成为三元组一部分的元素。

代码实现

代码随想录算法训练营第六天 哈希表part2|454.四数相加II 383. 赎金信 15. 三数之和_第8张图片

代码随想录算法训练营第六天 哈希表part2|454.四数相加II 383. 赎金信 15. 三数之和_第9张图片

你可能感兴趣的:(算法,散列表,数据结构)