代码随想录算法训练营第七天| LeetCode454.四数相加II | 383. 赎金信 | 15. 三数之和 | 18. 四数之和| 回顾双指针题目

目录

题目链接:454. 四数相加 II - 力扣(LeetCode)

题目链接:383. 赎金信 - 力扣(LeetCode)

题目链接:15. 三数之和 - 力扣(LeetCode)

题目链接:18. 四数之和 - 力扣(LeetCode)

回顾双指针法题目


  You are back it with all the good things in the world.  

今日体会:HashMap数组双指针法解题的巧妙。

题目链接:454. 四数相加 II - 力扣(LeetCode)

使用哈希表来优化这个问题:

1. 用一个哈希表来存储 nums1[i] + nums2[j] 的所有可能结果及其出现次数;

2. 遍历 nums3[k] + nums4[l] 的所有可能结果,并检查 -(nums3[k] + nums4[l]) 是否在哈希表中;

3. 如果存在,则累加对应的次数。

class Solution454 {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int n = nums1.length;
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        int count = 0;

        // 遍历nums1和nums2的所有组合,并记录和出现的次数
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                int sum = nums1[i] + nums2[j];
                hashMap.put(sum, hashMap.getOrDefault(sum,0) + 1);  // 如果指定的键在哈希表中存在,则返回对应的值;如果不存在,则返回指定的默认值0。
            }
        }

        // 遍历nums3和nums4的所有组合,检查是否存在和为0的情况
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                int sum = nums3[i] + nums4[j];
                count += hashMap.getOrDefault(-sum,0);
            }
        }

        return count;
    }
}
  • 时间复杂度:O(n^2)

题目链接:383. 赎金信 - 力扣(LeetCode)

思路1:可以使用一个数组来实现字符计数。因为题目中的字符都是小写字母,可以用一个长度为 26 的整型数组来表示每个字母的出现次数。

思路2:可以使用哈希表来记录 magazine 中每个字符的出现次数,然后检查 ransomNote 中的每个字符是否都在 magazine 中有足够的数量。

class Solution383 {
    public boolean canConstruct(String ransomNote, String magazine) {
        if (ransomNote.length() > magazine.length())
            return false;
        int[] arr = new int[26];

        // 记录magazine里各个字符出现的次数
        for (char c : magazine.toCharArray()) {
            arr[c - 'a'] += 1;
        }

        // 检查ransomNote中每个字符是否都在magazine中有足够的数量
        for (char c : ransomNote.toCharArray()) {
            if (arr[c - 'a'] == 0) {
                return false;
            }
            arr[c - 'a']--;
        }

        return true;
    }

    // 哈希表思路
    public boolean canConstruct2(String ransomNote, String magazine) {
        HashMap<Character, Integer> charCount = new HashMap<>();

        // 统计 magazine 中每个字符的数量
        for (char c : magazine.toCharArray()) {
            charCount.put(c, charCount.getOrDefault(c, 0) + 1);
        }

        // 检查 ransomNote 中的每个字符是否都在 magazine 中有足够的数量
        for (char c : ransomNote.toCharArray()) {
            if (!charCount.containsKey(c) || charCount.get(c) == 0) {
                return false;
            }
            charCount.put(c, charCount.get(c) - 1);
        }

        return true;
    }
}
  • 时间复杂度: O(n)

另 代码随想录 中:一些同学可能想,用数组干啥,都用map完事了,其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!

题目链接:15. 三数之和 - 力扣(LeetCode)

两层for循环就可以确定 a 和b 的数值了,可以使用哈希法来确定 0-(a+b) 是否在 数组里出现过,其实这个思路是正确的,但是有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组

这道题目使用哈希法并不十分合适,因为在去重的操作中有很多细节需要注意,在面试中很难直接写出没有bug的代码。

思路:故如图所示使用双指针法。首先将数组排序,然后遍历数组,对于每个元素,使用双指针在剩余部分寻找两个数使得三个数的和为0。为了避免重复的三元组,注意跳过重复的元素。

class Solution15 {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums); // 先排序 使用双指针法
        List<List<Integer>> result = new ArrayList<>();

        for (int i = 0; i < nums.length; i++) {
            if(i > 0 && nums[i] == nums[i-1]){
                continue;  // 跳过重复的元素
            }
            int left = i + 1, right = nums.length - 1;
            while (left < right){
                int sum = nums[i] + nums[left] + nums[right];
                if (sum == 0){
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    // 跳过重复的元素
                    while (left < right && nums[left] == nums[left + 1]){
                        left ++;
                    }
                    while (left < right && nums[right] == nums[right - 1]){
                          right --;
                    }
                    left ++;
                    right --;
                } else if(sum < 0){
                    left ++;
                } else {
                    right --;
                }
            }
        }
        return result;
    }
}

题目链接:18. 四数之和 - 力扣(LeetCode)

思路:使用双指针方法,类似于三数之和问题。首先对数组进行排序,然后遍历数组,对每一对组合,使用双指针在剩下的部分寻找两个数使得四者之和等于目标值。

1. 外层的两个循环分别固定两个数 nums[i]nums[j];

2. 内层的双指针 leftright 在剩余的数组部分寻找另外两个数使得四个数的和等于目标值;

3. 通过跳过重复的元素来避免重复的四元组。

class Solution18 {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums); // 排序 再用双指针法
        List<List<Integer>> result = new ArrayList<>();

        int n = nums.length;
        for (int i = 0; i < n - 3; i++) {  // 思考为什么是n-3?
            if (i > 0 && nums[i] == nums[i - 1])
                continue;

            for (int j = i + 1; j < n - 2; j++) { // 思考为什么是n-2?
                if (j > i + 1 && nums[j] == nums[j - 1])  // 去重
                    continue;
                int left = j + 1, right = n - 1;
                while (left < right){
                    long sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum == target){
                        result.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
                        // 去重
                        while (left < right && nums[left] == nums[left + 1]){
                            left ++;
                        }
                        while (left < right && nums[right] == nums[right - 1]){
                            right -- ;
                        }

                        left ++;
                        right --;
                    } else if (sum < target) {
                        left ++;
                    } else {
                        right --;
                    }
                }
            }
        }
        return result;
    }
}
  • 时间复杂度:O(n^3)

回顾双指针法题目

最后在这里回归一下使用了双指针法的几道题目:

双指针法将时间复杂度:O(n^2)的解法优化为 O(n)的解法。也就是降一个数量级,题目如下:

27. 移除元素 - 力扣(LeetCode)

15. 三数之和 - 力扣(LeetCode)

18. 四数之和 - 力扣(LeetCode)

链表相关双指针题目:

206. 反转链表 - 力扣(LeetCode)

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

面试题 02.07. 链表相交 - 力扣(LeetCode)

142. 环形链表 II - 力扣(LeetCode)

  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值