454.四数相加II
题目链接:454.四数相加II
题目讲解:代码随想录
题目描述:给你四个整数数组
nums1
、nums2
、nums3
和nums4
,数组长度都是n
,请你计算有多少个元组(i, j, k, l)
能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
解题思路:最简单的思路是四层for循环,但是这样会超时,时间复杂度为O()。
我们可以把4个数组分成2组,在每组中使用map记录2个数组元素的和以及出现的次数,故map的key为2个数组中元素的和,value为元素和出现的次数。
在第一组中记录v1+v2。在第二组中记录-v3-v4。使v1+v2 = -v3-v4,即v1+v2+v3+v4 = 0。
// 时间复杂度:O(n^2)
func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {
m := make(map[int]int)
count := 0
// 构建nums1和nums2的和的map
for _, v1 := range nums1{
for _, v2 := range nums2{
m[v1+v2]++
}
}
// 遍历nums3和nums4,检查-c-d是否在map中
for _, v3 := range nums3{
for _, v4 := range nums4{
sum := -v3 - v4
if countVal, ok := m[sum]; ok{
count += countVal
}
}
}
return count
}
383. 赎金信
题目链接:383. 赎金信
题目讲解:代码随想录
题目描述:给你两个字符串:
ransomNote
和magazine
,判断ransomNote
能不能由magazine
里面的字符构成。如果可以,返回true
;否则返回false
。magazine
中的每个字符只能在ransomNote
中使用一次。
本题判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成,但是这里需要注意两点。
-
第一点“为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思” 这里说明杂志里面的字母不可重复使用。
-
第二点 “你可以假设两个字符串均只含有小写字母。” 说明只有小写字母,这一点很重要。
使用map做:
// 时间复杂度: O(n)
func canConstruct(ransomNote string, magazine string) bool {
m := make(map[rune]int)
for _, r := range magazine{
m[r]++
}
for _, r := range ransomNote{
if _, ok := m[r]; !ok{
return false
}else{ // 如果存在
m[r]--
}
if m[r] < 0{
return false
}
}
return true
}
本题在使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
// 时间复杂度: O(n)
func canConstruct(ransomNote string, magazine string) bool {
record := make([]int, 26)
for _, v := range magazine{
record[v - rune('a')]++
}
for _, v := range ransomNote{
record[v - rune('a')]--
if record[v - rune('a')] < 0{
return false
}
}
return true
}
15. 三数之和
题目链接:15. 三数之和
题目讲解:代码随想录
题目描述:给你一个整数数组
nums
,判断是否存在三元组[nums[i], nums[j], nums[k]]
满足i != j
、i != k
且j != k
,同时还满足nums[i] + nums[j] + nums[k] == 0
。请你返回所有和为0
且不重复的三元组。注意:答案中不可以包含重复的三元组。
这道题推荐使用双指针来做
- 首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。
- 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
- 如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
还要注意一些细节:
- 因为已经是排序过的数组,所以当数组第一个元素大于0时,应直接返回。
- a的去重
-
b、c的去重
a的去重:
a 如果重复了怎么办,a是nums里遍历的元素,那么应该直接跳过去。
但这里有一个问题,是判断 nums[i] 与 nums[i + 1]是否相同,还是判断 nums[i] 与 nums[i-1] 是否相同?
如果使用if nums[i] == nums[i + 1]{跳过},这样的话可能会漏掉一些结果。例如当数组为[-1, -1, 2, 3]。当遍历到第一个-1时,同时判断第二个数组元素也为-1,则直接跳过{-1,-1, 2},显然不合理。
如果使用if nums[i] == nums[i-1]{跳过},当遍历到第一个-1时,不会进行操作,后续b和c会继续进行遍历,然后进行a+b+c==0的判断。当遍历到第二个-1时,判断与前一个数组元素值相同,则直接跳过,因为a=-1的情况已经进行判断过,所以第二个a=-1时,则不需要考虑。
b、c的去重:(a暂时固定)
例如数组的元素为:[-1,-1,-1,1,1,1,1,1,2,2,2,2],初始时,a为第一个-1,b为第二个-1,c为最后一个2,满足条件[-1, -1, 2]。后续遍历时,当b==-1时,要持续往后移动,当c==2时,要持续往前移动。(a值当前已经固定,b和c再出现重复的值没有意义)
// 时间复杂度:O(n^2)
func threeSum(nums []int) [][]int {
sort.Ints(nums)
res := [][]int{}
for i := 0; i < len(nums) - 2; i++{
n1 := nums[i]
if n1 > 0{
break
}
// 去重
if i > 0 && n1 == nums[i - 1]{
continue
}
l, r := i+1, len(nums) - 1
for l < r{
n2, n3 := nums[l], nums[r]
if n1 + n2 + n3 == 0{
res = append(res, []int{n1, n2, n3})
for l < r && nums[l] == n2{
l++
}
for l < r && nums[r] == n3{
r--
}
}else if n1 + n2 + n3 < 0{
l++
}else{
r--
}
}
}
return res
}
18. 四数之和
题目链接:18. 四数之和
题目讲解:代码随想录
题目描述:给你一个由
n
个整数组成的数组nums
,和一个目标值target
。请你找出并返回满足下述全部条件且不重复的四元组[nums[a], nums[b], nums[c], nums[d]]
(若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a
、b
、c
和d
互不相同nums[a] + nums[b] + nums[c] + nums[d] == target
思路其实和三数之和相似,只是多了一层循环。
但是有一些需要注意的点:不要判断nums[k] > target
就返回了,三数之和 可以通过 nums[i] > 0
就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。比如:数组是[-4, -3, -2, -1]
,target
是-10
,不能因为-4 > -10
而跳过。
// 时间复杂度:O(n^3)
func fourSum(nums []int, target int) [][]int {
if len(nums) < 4{
return nil
}
sort.Ints(nums)
var res [][]int
for i := 0; i < len(nums) - 3;i++{
n1 := nums[i]
if i > 0 && n1 == nums[i - 1]{
continue
}
for j := i+1; j < len(nums) - 2; j++{
n2 := nums[j]
if j > i+1 && n2 == nums[j-1]{
continue
}
l := j + 1
r := len(nums) - 1
for l < r{
n3 := nums[l]
n4 := nums[r]
sum := n1 + n2 + n3 + n4
if sum < target{
l++
}else if sum > target{
r--
}else{
res = append(res, []int{n1, n2, n3, n4})
for l < r && n3 == nums[l+1]{
l++
}
for l < r && n4 == nums[r-1]{
r--
}
r--
l++
}
}
}
}
return res
}