454. 四数相加 II
思路:暴力解法那就是四个for循环去遍历了,时间复杂度是n的四次方。优化:要注意这个题的要求,是计算有多少个元组,也就是不需要去重,如果四个数组都全零,那各个数组的各个位置都可以算。所以这个题可以先把第一和第二个数组各个元素的和放到一个hashmap里,key存的是和,value存的是该和出现的次数。然后把第三个和第四个数组各元素的和放到另一个hashmap里,这样其实就是去找hashmap1里面和hashmap2里面相加为0的元素,把他们的value相乘。
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int n = nums1.length;
HashMap<Integer,Integer> hashmap1 = new HashMap<>();
HashMap<Integer,Integer> hashmap2 = new HashMap<>();
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
hashmap1.put(nums1[i]+nums2[j],hashmap1.getOrDefault(nums1[i]+nums2[j],0)+1);
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
hashmap2.put(nums3[i]+nums4[j],hashmap2.getOrDefault(nums3[i]+nums4[j],0)+1);
}
}
int res = 0;
for(Integer key : hashmap1.keySet()){
if(hashmap2.keySet().contains(-1*key)){
res += hashmap1.get(key)*hashmap2.get(-1*(int)key);
}
}
return res;
}
优化
进一步可以继续优化,hashmap 存放数组1,2的和以及出现的次数,遍历数组3,4 找和他们相加为0的和在hashmap中出现的次数相加。
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int res = 0;
HashMap<Integer,Integer> hashmap = new HashMap<>();
for(int i:nums1){
for(int j:nums2){
int sum = i+j;
hashmap.put(sum,hashmap.getOrDefault(sum,0)+1);
}
}
for(int i:nums3){
for(int j:nums4){
res+=hashmap.getOrDefault(0-i-j,0);
}
}
return res;
}
383. 赎金信
用HashMap
把magazine的每个字符和出现次数放入hashMap(因为杂志字符串中的每个字符只能在赎金信字符串中使用一次,所以得记录次数),遍历ransomNote
public boolean canConstruct(String ransomNote, String magazine) {
HashMap<Character,Integer> hashMap = new HashMap<>();
for(char c : magazine.toCharArray()){
hashMap.put(c,hashMap.getOrDefault(c,0)+1);
}
for(char c : ransomNote.toCharArray()){
int count = hashMap.getOrDefault(c,0);
if(count<=0){
return false;
}else{
hashMap.put(c,count-1);
}
}
return true;
}
用数组
public boolean canConstruct(String ransomNote, String magazine) {
// shortcut
if (ransomNote.length() > magazine.length()) {
return false;
}
// 定义一个哈希映射数组
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. 三数之和
这个题用双指针的思路比较好,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。
如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
public List<List<Integer>> threeSum(int[] nums) {
//排序
Arrays.sort(nums);
List<List<Integer>> integers = new ArrayList<>();
int n = nums.length;
for (int i = 0; i < n-2; i++) {
int x = nums[i];
//对元素去重,注意这里是要用 nums[i] 和 nums[i-1]比,因为如果nums[i]和nums[i+1]比这样就侵占了左指针的范围
if ( i >0 &&x==nums[i-1]){
continue;
}
int lPoint = i+1;
int rPoint = n-1;
//这里是<,因为如果等于那就找成两个元素了
while (lPoint<rPoint){
int sum = x+nums[lPoint] + nums[rPoint];
if (sum>0){
//排好序的,找大了让右指针往回走
rPoint--;
} else if (sum<0) {
//找小了,让左指针往右走
lPoint++;
}else{
//找到了,保存
integers.add(List.of(x,nums[lPoint],nums[rPoint]));
for (++lPoint; lPoint < rPoint && nums[lPoint] == nums[lPoint - 1]; ++lPoint); // 跳过重复数字
for (--rPoint; rPoint > lPoint && nums[rPoint] == nums[rPoint + 1]; --rPoint); // 跳过重复数字
}
}
}
return integers;
}
18. 四数之和
四数之和和上面的三数之和思路是一样的,只不过是再套一层for循环,多遍历一个数,然后把三数之和的逻辑稍微改改放到for里面。
四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3)
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 (i > 0 && nums[i - 1] == nums[i]) { // 对nums[i]去重
continue;
}
for (int j = i + 1; j < nums.length; j++) {
if (j > i + 1 && nums[j - 1] == nums[j]) { // 对nums[j]去重
continue;
}
int left = j + 1;
int right = nums.length - 1;
while (right > left) {
// nums[k] + nums[i] + nums[left] + nums[right] > target int会溢出
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]));
// 对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;
}