代码随想录算法训练营第六天

LeetCode题目

  • 454. 四数相加 II
  • 383. 赎金信
  • 15. 三数之和
  • 18. 四数之和
  • 2140. 解决智力问题(每日打卡)
  • 总结
  • 往期打卡


454. 四数相加 II

跳转: 454. 四数相加 II

问题:

给你四个整数数组 nums1nums2nums3nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

思路:

将四层for循环拆成两半统计求和

复杂度:

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( n ) O(n) O(n)

代码:

class Solution {
	public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
		int n = nums1.length;
		Map<Integer, Integer> map = new HashMap<>();
		int res = 0;
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				map.merge(-(nums1[i] + nums2[j]), 1, Integer::sum);
			}
		}
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				if(map.containsKey(nums3[i]+nums4[j])) {
					res+=map.get(nums3[i]+nums4[j]);
				}
			}
		}
		return res;
	}
}

383. 赎金信

跳转: 383. 赎金信

问题:

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

思路:

计数相消

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

代码:

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        if (ransomNote.length() > magazine.length())
            return false;
        int[] record = new int[26];
        for (char c : magazine.toCharArray()) {
            record[c - 'a']++;
        }
        for (char c : ransomNote.toCharArray()) {
            int i = c - 'a';
            record[i]--;
            if(record[i]<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 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

思路:

方法一,哈希
需要排序后剪枝,暴力2n过不了
可以按顺序加权算哈希去重
也可以排序后在遍历中去重

方法二,双指针
一层一层遍历,按照和动态的控制左右指针收缩.

复杂度:

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( 1 ) O(1) O(1)

代码(双指针):

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        int n = nums.length;
        for(int i = 0;i<n-2;i++){
            if(nums[i]>0) break;
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int l = i+1;
            int r = n-1;
            while(l<r){
                int sum = nums[i]+nums[l]+nums[r];
                if(sum>0) r--;
                else if(sum<0) l++;
                else{
                    result.add(Arrays.asList(nums[i], nums[l], nums[r]));
                    while (r > l && nums[r] == nums[r - 1]) r--;
                    while (r > l && nums[l] == nums[l + 1]) l++;
                    r--; 
                    l++;
                }
            }
        }
        return result;
    }
}

代码(哈希):

class Solution {
	public List<List<Integer>> threeSum(int[] nums) {
		List<List<Integer>> result = new ArrayList<>();
		Arrays.sort(nums);
		if (nums[0] > 0)
			return result;
		int n = nums.length;
		for (int i = 0; i < n; i++) {
			if (nums[i] > 0)
				break;
			if (i>0&&nums[i] == nums[i - 1]) {
				continue;
			}
			Set<Integer> set = new HashSet<>();
			for (int j = i + 1; j < n; j++) {
				if (j > i + 2 && nums[j] == nums[j - 1]&&nums[j]==nums[j-2]){
                    continue;
                }
				int key = -nums[i] - nums[j];
				 
				if (set.contains(key)) {
					result.add(Arrays.asList(key,nums[i],nums[j]));
					set.remove(key);
				} else {
					set.add(nums[j]);
				}
			}
		}
		return result;
	}
}

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
你可以按 任意顺序 返回答案 。

思路:

两层for循环加双指针,需要排序和去重(去掉相等的重复值),可以加上终止判断剪枝

复杂度:

  • 时间复杂度: O ( n 3 ) O(n^3) O(n3)
  • 空间复杂度: O ( 1 ) O(1) O(1)

代码:

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
		List<List<Integer>> result = new ArrayList<>();
		Arrays.sort(nums);
		int n = nums.length;
		for(int i = 0;i<n;i++) {
            if(nums[i]>target&&nums[i]>=0) break;
			if(i>0&&nums[i]==nums[i-1]) continue;
			for(int j=i+1;j<n;j++) {
                if (nums[j] + nums[i] > target && nums[j] + nums[i] >= 0) {
                    break;
                }
				if(j>i+1&&nums[j]==nums[j-1]) continue;
				int l = j+1;
				int r = n-1;
				while(l<r) {
					long key = (long)nums[i]+nums[j]+nums[l]+nums[r];
					if(key<target) l++;
					else if(key>target) r--;
					else {
						result.add(Arrays.asList(nums[i],nums[j],nums[l],nums[r]));
						while(l<r&&nums[l]==nums[l+1]) l++;
						while(l<r&&nums[r]==nums[r-1]) r--;
						l++;
						r--;
					}
				}
			}
		}
		return result;
	}
}

2140. 解决智力问题(每日打卡)

跳转: 2140. 解决智力问题

问题:

给你一个下标从 0 开始的二维整数数组 questions ,其中 questions[i] = [pointsi, brainpoweri]

这个数组表示一场考试里的一系列题目,你需要 按顺序 (也就是从问题 0 开始依次解决),针对每个问题选择 解决 或者 跳过 操作。解决问题 i 将让你 获得 pointsi 的分数,但是你将 无法 解决接下来的 brainpoweri 个问题(即只能跳过接下来的 brainpoweri 个问题)。如果你跳过问题 i ,你可以对下一个问题决定使用哪种操作。

  • 比方说,给你 questions = [[3, 2], [4, 3], [4, 4], [2, 5]]

    • 如果问题 0 被解决了, 那么你可以获得 3 分,但你不能解决问题 12
    • 如果你跳过问题 0 ,且解决问题 1 ,你将获得 4 分但是不能解决问题 23

请你返回这场考试里你能获得的 最高 分数。

思路:

动态规划
可以根据当前步选与不选做更新
也可以每步更新,然后只保留最大值,逐级传递

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

代码:

class Solution {
    public long mostPoints(int[][] questions) {
        int n = questions.length;       
        long[] dp = new long[n + 1];
        for (int i = 0; i < n; i++) {
            int score = questions[i][0];
            int skip = questions[i][1];
            int nextPos = Math.min(i + skip + 1, n); 
            dp[nextPos] = Math.max(dp[nextPos], dp[i] + score);
            dp[i + 1] = Math.max(dp[i + 1], dp[i]);
        }
        return dp[n];
    }
}

代码(二维dp数组)

class Solution {
    public long mostPoints(int[][] questions) {
        int n = questions.length;
        long[][] dp = new long[n+1][2];
        for (int i = 0; i < n; i++) {
            int next_pos = Math.min(i + questions[i][1] + 1, n);
            dp[i+1][0] = Math.max(dp[i][0], dp[i][1]);
            dp[next_pos][1] = Math.max(dp[next_pos][1], dp[i + 1][0] + questions[i][0]);
        }
        return Math.max(dp[n][0], dp[n][1]);
    }
}

总结

练习了哈希表与排序双指针拆分计算简化复杂度
以及动态规划

往期打卡

代码随想录算法训练营第一天
代码随想录算法训练营第二天
代码随想录算法训练营第三天
代码随想录算法训练营第四天
代码随想录算法训练营周末一
代码随想录算法训练营第五天

你可能感兴趣的:(代码随想录打卡,算法)