leetcode算法练习
454.四数相加II
题目链接
思路:
- 将四个数组分为A、B、C、D 遍历取前两个数组中的数为a+b 放入集合中 后两个数组中的数为c+d 判断能实现target=0时 c+d所需的匹配数 是否在集合中出现过 即考虑0-(c+d)是否在集合中
- 考虑使用什么数据结构? 本题中数值可能会较大 利用数组下标做映射可能不够 故数组pass 同时 不但要考虑a+b在集合中是否出现过 还要考虑到a+b出现过多少次 这样才能和c+d作映射 故考虑使用map(key为a+b value为a+b出现的次数)
- 最后统计有多少组符合条件的 在累加时 加的是value的值 而不是每次+1
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
// 设定存放值和次数的map
Map<Integer, Integer> map = new HashMap<>();
int temp;
int res = 0;
// 统计两个数组中的元素之和 同时统计出现的次数 放入map
// 遍历第一个数组
for (int i : nums1) {
// 遍历第二个数组
for (int j : nums2) {
// 记录下元素之和
temp = i + j;
// 判断该元素之和是否已经存在于map中
if (map.containsKey(temp)) {
// 如若已经存在 将该元素对应的value +1
map.put(temp, map.get(temp) + 1);
} else {
// 若还不存在 将元素之和作为key存入 value为1 代表出现次数
map.put(temp, 1);
}
}
}
// 统计剩余的两个元素的和 在map中找是否存在相加为0的情况 同时记录次数
for (int i : nums3) {
for (int j : nums4) {
temp = i + j;
// 查找目标值 即之前说明的0- (c + d)
if (map.containsKey(0 - temp)) {
// 次数要加对应的value
res += map.get(0 - temp);
}
}
}
return res;
}
}
383. 赎金信
思路:
判断元素是否出现过之间联想到哈希法 本题的数值范围为a~z 故可以考虑使用数组来解决——a对应到数组下标为0的位置 z对应到数组下标为25的位置 遍历第一个字符串出现的字母频率 存入定义好的record[26]数组中 遍历第二个字符串每个字母的出现频率 对应地到record数组中做减法
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
// 定义一个哈希映射数组
int[] record = new int[26];
// 遍历第一个字符 对应作加法
for(char c : magazine.toCharArray()){
record[c - 'a'] += 1;
}
// 遍历第二个字符 对应作减法
for(char c : ransomNote.toCharArray()){
record[c - 'a'] -= 1;
}
// 如果数组中存在负数,说明ransomNote字符串总存在magazine中没有的字符
for(int i : record){
if(i < 0){
return false;
}
}
return true;
}
}
15. 三数之和
题目链接
注意:
三元组是要去重的——本题去重较复杂
思路:
-
本题即找到a+b+c=0 若使用哈希法:两个for循环 第一个for确定a 第二个for确定b 然后在数组中查询0-(a+b)是否出现过 利用map来做映射 但因为a b c都要去重 使用哈希法比较麻烦 因为哈希法在寻找c时是没有什么顺序的
-
选择使用双指针法较为合适 因为三元组要去重
-
首先要先对数组进行排序 创建三个指针i left right(i表示a的下标 left表示b的下标 right表示c的下标) 第一个for循环来控制i i表示现在遍历的元素 从左至右 ——当nums[i]+nums[left]+nums[right] > 0时 意味着数据大了 应该缩小 故让right往左边移动一位;当nums[i]+nums[left]+nums[right] < 0时 意味着数据小了 应该扩大 故让left往右边移动一位;当nums[i]+nums[left]+nums[right] = 0时 获取结果
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++) {
// 因为已经排过序了 如果第一个数就已经>0 那不可能后面再加=0
if (nums[i] > 0) {
return result;
}
// 注意判断是num[i] == num[i+1] 还是num[i] == num[i-1]
// 例如:-1 -1 2
// i left right
//如果num[i] == num[i+1] 那么前-1 == -1 变成判断集合内元素是否有2重复
//实际上是要判断 集合是否有重复 故应该选择num[i] == num[i-1] 这样当后-1 == 前-1 才意味着 重复了——可以理解为:a现在是第一个数 如果第一个数确定了 那么其left right也确定了 所以a要和之前已经记录过的元素不一样
// 对a去重
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
// 初始化left和right 各自向中心移动
int left = i + 1;
int right = nums.length - 1;
// 应该是right >= left 还是right > left——现在是求三个数 若left=right 那么就是两个数了 故left != right
while (right > left) {
int sum = nums[i] + nums[left] + nums[right];
// 若三数之和>0;<0;=0
if (sum > 0) {
right--;
} else if (sum < 0) {
left++;
} else {
// 把三个数初始成一个数组 放入result数组中
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 对结果去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
right--;
left++;
}
}
}
return result;
}
}
18. 四数之和
题目链接
思路:
- 在三数之和的基础上 再多加一个for循环 即i j left right
- 主要难点在剪枝和去重的操作:
- 首先不能像三数之和一样 刚开始就单纯判断nuns[i]>target剪枝 因为target是任意输入的 同时数组内的数可能<0 例如:target=-5 排序后数组为 -4 -1 0 0 那么nums[i] = -4 > target 但是明显是有符合条件的数组的——故利用if(nums[i] > 0 && nums[i] > target) break;
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] > 0 && nums[i] > target) {
return result;
}
// 和三数之和一样的去重逻辑
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < nums.length; j++) {
// 2级剪枝处理
if (nums[i] + nums[j] > target && nums[i] + nums[j] >= 0) {
break;
}
// 对j进行去重
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
int left = j + 1;
int right = nums.length - 1;
while (right > left) {
long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
if (sum > target) {
right--;
} else if (sum < target) {
left++;
} else {
result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
left++;
right--;
}
}
}
}
return result;
}
}