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

day07:哈希表- 454.四数相加II383. 赎金信15. 三数之和18. 四数之和

LeetCode 454.四数相加II

题目链接:

四数相加II

文章讲解:

https://programmercarl.com/0454.%E5%9B%9B%E6%95%B0%E7%9B%B8%E5%8A%A0II.html

视频讲解:

https://www.bilibili.com/video/BV1Md4y1Q7Yh/

思路和解法:

哈希表:

如果采用暴力解法,时间复杂度为 O ( n 4 ) O(n^4) O(n4) ,效率很低,所以考虑其他方法。

题目说的是四个独立数组,所以只要找到nums1[i] + nums2[j] + nums3[k] + nums4[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况

1、首先定义一个Dictionary,key存放nums1、num2两个数组中所有和的情况,对应的value是和出现的次数。

2、开始遍历nums1和nums2,统计两个数组元素之和以及和出现的次数,放到Dictionary中。

3、定义一个int变量res存储nums1[i] + nums2[j] + nums3[k] + nums4[l] = 0出现的次数。

4、再遍历nums3和nums4,如果在Dictionary中找到了0-(nums3[k] + nums4[l]),就用res把Dictionary中key对应的value加上。

之所以要在Dictionary中找0-(nums3[k] + nums4[l]),是因为题目中要求找nums1[i] + nums2[j] + nums3[k] + nums4[l] = 0的情况,Dictionary中已经存放了nums1[i] + nums2[j]nums1[i] + nums2[j] = 0 - (nums3[k] + nums4[l]),所以只有在Dictionary中找到符合**0-(nums3[k] + nums4[l])**的数,才能符合条件。

5、最后返回res

public class Solution {
    public int FourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Dictionary<int, int> dict = new Dictionary<int, int>();
        foreach(int i in nums1)
        {
            foreach(int j in nums2)
            {
                int sum = i + j;
                if(dict.ContainsKey(sum))
                {
                    dict[sum]++;
                }
                else
                {
                    dict.Add(sum,1);
                }
            }
        }

        int res = 0;
        foreach(int k in nums3)
        {
            foreach(int l in nums4)
            {
                int sum = k + l;
                if(dict.TryGetValue(-sum, out int result))
                {
                    res += result;
                }
            }
        }
        return res;
    }
}

LeetCode 383. 赎金信

题目链接:

383. 赎金信

文章讲解:

https://programmercarl.com/0383.%E8%B5%8E%E9%87%91%E4%BF%A1.html

思路和解法:

哈希表

这题是242.有效的字母异位词 的变种,可以采用相同思路,同样使用哈希表来做。

1、题目中明确了ransomNote(以下采用A) 和 magazine(以下采用B) 由小写英文字母组成,且当A能用B里面的字符组成时才返回true,典型的在B中查找所需元素的问题,而且又因为明确小写字母构成,数据数量固定,所以采用数组,数组存放的是下标对应的字母出现的次数,比如a对应下标0,b对应1…z对应25等。数组初始化为0。

2、先遍历B中的字母,每个字母出现一次,字母对应下标的对应数据+1。

3、再遍历A中的字母,每个字母出现一次,字母对应下标的对应数据-1。

4、遍历结束后,因为是要判断A能不能由B里面的数字组成,所以只要判断数组中有没有数据小于0就可以,如果都大于或等于0,说明B中的字母可以组成A,返回true,反之返回false。

public class Solution {
    public bool CanConstruct(string ransomNote, string magazine) {
        int rl = ransomNote.Length;
        int ml = magazine.Length;
        if(rl > ml)
            return false;
        int[] a = new int[26];
        for(int i = 0; i < ml; i++)
        {
            a[magazine[i] - 'a']++;
        }
        for(int j = 0; j < rl; j++)
        {
            a[ransomNote[j] - 'a']--;
        }
        foreach(var i in a)
        {
            if(i < 0)
                return false;
        }

        return true;
    }
}

LeetCode 15. 三数之和

题目链接:

15. 三数之和

文章讲解:

https://programmercarl.com/0015.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.html

视频讲解:

https://www.bilibili.com/video/BV1GW4y127qo/

思路和解法:

哈希表:

两层for循环确定a和b的数值,然后用哈希法来确定0-(a+b) 是否在数组里出现过,但问题就在题目要求不能有重复值,就是要去重,有很多细节要注意,很麻烦。所以考虑其他方法。

双指针:

1、以这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

2、在数组中找到 abc 使得a + b +c =0,这里相当于 a = nums[i],b = nums[left],c = nums[right]。

3、移动left和right:

如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。

如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。

4、去重是重点,因为比如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断 下一个也是-1,那这组数据就pass了。所以我们要做的是 不能有重复的三元组,但三元组内的元素是可以重复的!

public class Solution {
    public IList<IList<int>> ThreeSum(int[] nums) {
        var result = new List<IList<int>>();

        Array.Sort(nums);

        for(int i = 0; i < nums.Length - 2; i++)
        {
            int a = nums[i];

            //因为已经对数组进行了排序,如果第一个数就大于0,那么后续的数都大于0,绝对没有符合条件的数
            if(a > 0)
                break;
            
            //去重 a
            if(i > 0 && a == nums[i - 1])
                continue;
            
            int left = i + 1;
            int right = nums.Length - 1;
            while(left < right)
            {
                int b = nums[left];
                int c = nums[right];
                int sum = a + b + c;
                if(sum > 0)
                {
                    right--;
                }
                else if(sum < 0)
                {
                    left++;
                }
                else
                {
                    result.Add(new List<int> {a, b, c});
					
                    //找到答案后,对left和right进行收缩,且收缩的时候要对bc进行去重
                    while(left < right && nums[left] == b)
                    {
                        left++;
                    }
                    while(left < right && nums[right] == c)
                    {
                        right--;
                    }
                }
            }
        }
        return result;
    }
}

LeetCode 18. 四数之和

题目链接:

18. 四数之和

文章讲解:

https://programmercarl.com/0018.%E5%9B%9B%E6%95%B0%E4%B9%8B%E5%92%8C.html

视频讲解:

https://www.bilibili.com/video/BV1DS4y147US/

思路和解法:

双指针:

和三数之和类似,也采用双指针的方法。

四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是 O ( n 2 ) O(n^2) O(n2),四数之和的时间复杂度是 O ( n 3 ) O(n^3) O(n3)

public class Solution {
    public IList<IList<int>> FourSum(int[] nums, int target) {
        var result = new List<IList<int>>();
        Array.Sort(nums);
        for(int i = 0; i < nums.Length - 3; i++)
        {
            int a = nums[i];

            if(a > 0 && a > target)
                break;

            //去重a
            if(i > 0 && a == nums[i - 1])
                continue;
            
            for(int j = i + 1; j < nums.Length - 2; j++)
            {
                int b = nums[j];
                //去重b
                if(j > i + 1 && b == nums[j - 1])
                    continue;

                int left = j + 1;
                int right = nums.Length - 1;
                while(left < right)
                {
                    int c = nums[left];
                    int d = nums[right];
                    int sum = a + b + c + d;
                    if(sum > target)
                    {
                        right--;
                    }
                    else if(sum < target)
                    {
                        left++;
                    }
                    else
                    {
                        result.Add(new List<int>{a, b, c, d});
                        while(left < right && nums[left] == c)
                        {
                            left++;
                        }
                        while(left < right && nums[right] == d)
                        {
                            right--;
                        }
                    }
                }
            }
        }

        return result;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值