四数相加II LeetCode 454
题目链接:四数相加II LeetCode 454 - 中等
思路
四数相加相当于 两个两数相加
int sum = 0;
// hash表key用来存nums1+nums的所有情况,value存这些情况出现的次数
Map<Integer,Integer> map = new HashMap<>();
// 记录
for (int i = 0; i < nums1.length; i++) {
for (int j = 0; j < nums2.length; j++) {
int temp = nums1[i] + nums2[j];
if (!map.containsKey(temp)){
map.put(temp, 1);
}else {
map.put(temp, map.get(temp) + 1);
}
}
}
// 判断nums3+nums4 ?= -nums1+num2,是就记录一次
for (int i = 0; i < nums3.length; i++) {
for (int j = 0; j < nums4.length; j++) {
int temp = nums3[i] + nums4[j];
if (map.containsKey(0 - temp)){
// 存在一次不是+1,是加num1+num2总共的次数,相当于相乘的关系
sum += map.get(-temp);
}
}
}
return sum;
}
总结
如果直接两两相加之后再相加的话本题是会超时的,时间复杂度为O(4n^2),所以我们把nums1和nums2的和存在一个map里面,再遍历另外两个数的和,若nums1+nums2 = -nums3+nums4,就相当于和为0。记nums1+nums2=a,nums3+nums4=b,每次遍历出现b=-a的情况,我们的sum += a,这里不是+1!因为b=-a的每一种情况都对应着nums1+nums2的a种情况。
赎金信 LeetCode 383
题目链接:赎金信 LeetCode 383 - 简单
思路
直接用一个map存magazine里面出现的每一个字母,出现一次给他+1,然后在遍历ransomNote,判断是不是每个字母都在map里面,是就-1,若map中存的该数字个位为0了或是不存在这个数都返回false。
public boolean canConstruct(String ransomNote, String magazine) {
// 用一个hash表来存magazine种出现的所有字母和次数
Map<Character, Integer> map = new HashMap<>();
// 第一个for循环存
for (Character c : magazine.toCharArray()){
if (!map.containsKey(c)){
map.put(c, 1);
}else {
map.put(c,map.get(c) + 1);
}
}
// 第二个for循环来消
for (Character c : ransomNote.toCharArray()){
// map种没有ransomNote的元素只返回false
if (!map.containsKey(c)){
return false;
}else if (map.get(c) == 0){ // map中需要的元素用完了也返回false
return false;
}else { // map还剩有就将数量-1
map.put(c, map.get(c) - 1);
}
}
// 恰好全部消完或者还剩有都返回true;
return true;
}
总结
这题和之前做的有效字母异为词差不多。看到这种题一开始就想到用hash来做,但是没有想到数据量比较少可以用数组来充当哈希表来做,这样的话会快很多,因为map底层要维护红黑树或哈希表,所有空间非常大,这样不划算!
示例代码:
// 用数组效率更高
public boolean canConstruct(String ransomNote, String magazine) {
int[] hash = new int[26];
for (char c : magazine.toCharArray()){
hash[c - 'a']++;
}
for (char c : ransomNote.toCharArray()){
hash[c - 'a']--;
}
for (char c : ransomNote.toCharArray()){
// hash表里可以还剩数据,就是magazine数据更多,但是小于0了一定是少了或者不存在某个字母
if (hash[c - 'a'] < 0){
return false;
}
}
return true;
}
三数之和 LeetCode 15
题目链接:三数之和 LeetCode 15 - 中等
思路
两数之和再加一个数,但多了一个去重的操作,可用双指针来解决。
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
// 先从小到大排序
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
// 对i去重
if (nums[i] > 0){
break;
}
if (i >0 && nums[i] == nums[i-1]){
continue;
}
// 每次得重置left与right,所有在for里面定义
int left = i + 1;
int right = nums.length - 1;
while (left < right){
int sum = nums[left] + nums[right] + nums[i];
if (sum < 0){
left++;
}else if(sum > 0){
right--;
}else {
res.add(Arrays.asList(nums[i],nums[left],nums[right]));
// 去重left 往右靠
while (left<right && nums[left] == nums[left+1]){
left++;
}
// 去重right 往左靠
while (left<right && nums[right] == nums[right-1]){
right--;
}
// 完成一轮,左右指针都往中靠近一步
left++;
right--;
}
}
}
return res;
总结
自己在完成这道题的时候大题的思路知道,但是细节不太会,也就是去重不太熟悉,经常考虑不周导致进入死循环,第二就是对api不熟练,对Arrays.asList(nums[i],nums[left],nums[right])这种创建list方式不熟,应多多复习!
四数之和 LeetCode 18
题目链接: 四数之和 LeetCode 18 - 中等
思路
就是上面三数之和(已去重)再多加一个数,可直接多一层for循环解决
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> res = new ArrayList<>();
// 一定要先排序
Arrays.sort(nums);
// 第一个数 nums[i]
for (int i = 0; i < nums.length; i++) {
// nums[i] > 0 正数往右才是越来越大,加起来更大,负数加起来更小
if (nums[i] > 0 && nums[i] > target) break;
// i去重 i剪纸
if (i>0 && nums[i] == nums[i-1]) continue;
// 第二个数 nums[j],从i+1开始
for (int j = i + 1; j < nums.length; j++) {
// j去重 j剪枝 ==> 必须满足j在i的右边
if (j > i + 1 && nums[j] == nums[j-1]) continue;
int left = j + 1;
int right = nums.length - 1;
while (left < right){
// 数字可能比较大,int可能会溢出!所以强转为long型
long sum =(long) nums[i] + nums[j] + nums[left] + nums[right];
if (sum > target) {
right--;
}else if (sum < target) {
left++;
}else {
res.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
// left与right去重
while (left < right && nums[left] == nums[left+1]) left++;
while (left < right && nums[right] == nums[right-1]) right--;
left++;
right--;
}
}
}
}
return res;
}
总结
本题若没有考虑(long) nums[i] + nums[j] + nums[left] + nums[right]这一步会出错,因为四个数相加会超过int的范围。另外就是对i剪枝时if (nums[i] > 0 && nums[i] > target) break;若没写nums[i] > 0就可能会直接跳出,因为正数是越加越大,负数越加越小。