454.四数相加II
思路:哈希法。使用map集合,key存放a+b的值,value存放a+b出现的次数。使用两层循环,循环前两个数组,找出a+b,对map赋值。再用两层循环,遍历后两个数组,找出符合map中符合目标的值,并通过value获取出现的次数并累加。(其实就是将四数相加变成两数相加,将时间复杂度从O(n4)降至O(n2))
时间复杂度:O(n2)
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int count = 0;
Map<Integer, Integer> map = new HashMap<>();
// 先遍历前两个数组,将a+b以及出现的次数放到map中。
for (int a : nums1) {
for (int b : nums2) {
map.put(a + b, map.getOrDefault(a + b, 0) + 1);
}
}
// 然后遍历后两个数组,从map中找到符合条件的a+b并计数
for (int c : nums3) {
for (int d : nums4) {
if (map.containsKey(-(c + d))) {
count += map.get(-(c + d));
}
}
}
return count;
}
}
383.赎金信
思路:哈希法。其实就是字母异位词的扩展题目,思路同字母异位词。
时间复杂度:O(n)
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
// 变向的字母异位词
int[] count = new int[26];
for (int i = 0; i < ransomNote.length(); i++) {
count[ransomNote.charAt(i) - 'a']++;
}
for (int i = 0; i < magazine.length(); i++) {
count[magazine.charAt(i) - 'a']--;
}
for (int i : count) {
if (i > 0) {
return false;
}
}
return true;
}
}
15.三数之和
思路:双指针法最优的解法。(本题的重点在于考察去重操作。)先对数组进行排序。使用i遍历一遍数组,遍历过程中,left初始为i+1,right初始为最后一个元素,然后如果left和right指向的元素符合目标值,将三个数放进结果中,如果不符合目标值,调整left和right的位置。(注意,要对三个数都进行去重操作。i指向的是a,如果和前一个元素重复,就没必要再进行遍历了,跳过执行下一个元素。left指向的是b,如果存入结果后,后一个元素仍然和当前元素相同,跳过后一个元素。right指向的是c,如果存入结果后,前一个元素仍然和当前元素相同,跳过前一个元素。)通过双指针将时间复杂度由O(n3)降至了O(n2)
时间复杂度:O(n2)
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
// 双指针法
List<List<Integer>> result = new ArrayList<>();
// 先进行排序
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) return result; // 如果当前最小的元素都大于0了,返回result就可以了。
if (i > 0 && nums[i] == nums[i - 1]) continue; // 对a去重,如果和前一个元素相同,说明已经判断过了,执行下一个。
int left = i + 1, right = nums.length - 1;
while (left < right) {
if (nums[i] + nums[left] + nums[right] > 0) {
right--;
} else if (nums[i] + nums[left] + nums[right] < 0) {
left++;
} else {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
while (left < right && nums[right] == nums[right - 1]) { // 对b去重
right--;
}
while (left < right && nums[left] == nums[left + 1]){ // 对c去重
left++;
}
right--;
left++;
}
}
}
return result;
}
}
记录一下哈希解法,并不是最优解,使用哈希法会导致去重操作十分复杂。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
// 哈希法
List<List<Integer>> result = new ArrayList<>();
// 同样经过排序操作更简单
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) return result;
if (i > 0 && nums[i] == nums [i - 1]) continue; // 对a去重
Set<Integer> set = new HashSet<>();
for (int j = i + 1; j < nums.length; j++) {
if (j > i + 2 && nums[j] == nums[j - 1] && nums[j - 1] == nums[j - 2]) continue; // 对b去重
// 连续三个相同才跳过。(个人认为前面有个需要当作c,所以只有连续两个相同的时候,c和b一样不应跳过)
int c = -(nums[i] + nums[j]);
if (set.contains(c)) {
result.add(Arrays.asList(nums[i], nums[j], c));
set.remove(c); // 对c去重
}
set.add(nums[j]);
}
}
return result;
}
}
8.四数之和
思路:使用双指针法,原理同三数之和。因为这次目标值可以指定为负数,所以要注意剪枝时的操作。用i和j确定a和b使用left和right寻找符合条件c和d。(同样要注意,这四个数,每个数都要进行去重操作。)通过双指针将时间复杂度由O(n3)降至了O(n2)
时间复杂度:O(n3)
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
// 双指针法,类似三数之和
List<List<Integer>> result = new ArrayList<>();
// 先排序
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (nums[i] > target && (nums[i] >=0 || target >= 0))
return result;
if (i > 0 && nums[i] == nums[i - 1]) { // 对a去重
continue;
}
for (int j = i + 1; j < nums.length; j++) {
if (j > i + 1 && nums[j] == nums[j - 1]) // 对b去重
continue;
int left = j + 1, right = nums.length - 1;
while (left < right) {
if (nums[i] + nums[j] + nums[left] + nums[right] > target) {
right--;
} else if (nums[i] + nums[j] + nums[left] + nums[right] < target) {
left++;
} else {
result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
while (left < right && nums[right] == nums[right - 1]) { // 对c去重
right--;
}
while (left < right && nums[left] == nums[left + 1]) { // 对d去重
left++;
}
right--;
left++;
}
}
}
}
return result;
}
}