跳转: 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循环拆成两半统计求和
复杂度:
代码:
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. 赎金信
问题:
给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
思路:
计数相消
复杂度:
代码:
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. 三数之和
问题:
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
思路:
方法一,哈希
需要排序后剪枝,暴力2n过不了
可以按顺序加权算哈希去重
也可以排序后在遍历中去重
方法二,双指针
一层一层遍历,按照和动态的控制左右指针收缩.
复杂度:
代码(双指针):
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. 四数之和
问题:
给你一个由 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循环加双指针,需要排序和去重(去掉相等的重复值),可以加上终止判断剪枝
复杂度:
代码:
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. 解决智力问题
问题:
给你一个下标从 0 开始的二维整数数组 questions
,其中 questions[i] = [pointsi, brainpoweri]
。
这个数组表示一场考试里的一系列题目,你需要 按顺序 (也就是从问题 0
开始依次解决),针对每个问题选择 解决 或者 跳过 操作。解决问题 i
将让你 获得 pointsi
的分数,但是你将 无法 解决接下来的 brainpoweri
个问题(即只能跳过接下来的 brainpoweri
个问题)。如果你跳过问题 i
,你可以对下一个问题决定使用哪种操作。
比方说,给你 questions = [[3, 2], [4, 3], [4, 4], [2, 5]]
:
0
被解决了, 那么你可以获得 3
分,但你不能解决问题 1
和 2
。0
,且解决问题 1
,你将获得 4
分但是不能解决问题 2
和 3
。请你返回这场考试里你能获得的 最高 分数。
思路:
动态规划
可以根据当前步选与不选做更新
也可以每步更新,然后只保留最大值,逐级传递
复杂度:
代码:
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]);
}
}
练习了哈希表与排序双指针拆分计算简化复杂度
以及动态规划
代码随想录算法训练营第一天
代码随想录算法训练营第二天
代码随想录算法训练营第三天
代码随想录算法训练营第四天
代码随想录算法训练营周末一
代码随想录算法训练营第五天