学习内容
力扣算法:
454.四数相加||
383.赎金信
15.三数之和
18.四数之和
具体内容
454.四数相加||
给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
示例 1:
输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2] 输出:2
解释: 两个元组如下:
- (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
- (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
示例 2:
输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0] 输出:1
提示:
n == nums1.length
n == nums2.length
n == nums3.length
n == nums4.length
1 <= n <= 200
-228 <= nums1[i], nums2[i], nums3[i], nums4[i] <= 228
做题思路
由于求得的元组是可重复的,因此思路与“两数之和”类似,先用hashmap获取前两个数组不同位置分别相加得到的值temp,再用0-temp判断后面两个数组不同位置分别相加是否有相对应的值;且由于返回元组个数,因此可以将前两个数组的值temp当作key,而相加得到的值temp在数组的次数当作value
解题
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
HashMap<Integer,Integer> map = new HashMap<>();
for(int i:nums1){
for(int j:nums2){
int temp = i+j;
if(map.containsKey(temp)){
map.put(temp,map.get(temp)+1);
}else{
map.put(temp,1);
}
}
}
//该变量返回的元组个数
int res = 0;
for(int i:nums3){
for(int j:nums4){
int temp = i+j;
if(map.containsKey(0-temp)){
res += map.get(0-temp);
}
}
}
return res;
}
}
解答代码存在的疑问
1、为什么不是直接返回map的key值对应的value,而是需要把满足的条件的value相加
因为有4个数相加,他的排列方式肯定不值一种,而我们需要把满足条件的每一种出现的次数进行相加,最终才能得到所有的元素
383.赎金信
给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。
示例 1:
输入:ransomNote = “a”, magazine = “b” 输出:false
示例 2:
输入:ransomNote = “aa”, magazine = “ab” 输出:false
示例 3:
输入:ransomNote = “aa”, magazine = “aab” 输出:true
提示:
1 <= ransomNote.length, magazine.length <= 105
ransomNote 和 magazine 由小写英文字母组成
做题思路
此题类似于“有效的字母异位词”,且有题目可得,ransomNote长度比magazine小且由magazine的元素组成,仍然可采用ASC||26为编码的思路,判断ransomNote每个字符出现的次数的是否小于magazine每个字符的次数
解题
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int res[] = new int[26];
for(int i = 0;i < magazine.length();i++){
res[magazine.charAt(i)-'a']++;
}
for(int j = 0;j < ransomNote.length();j++){
res[ransomNote.charAt(j)-'a']--;
}
for(int i:res){
if(i < 0){
return false;
}
}
return true;
}
}
解答代码存在的疑问
1、为什么当存放的数组元素比0时,说明ransomNote有字符是没有在magazine里面?
一开始元素都为0,后面对magazine进行操作,则对于其字符都会+1,而后对ransomNote进行操作,对ransomNote出现的字符则进行-1,因此要是字符在ransomNote出现而magezine不出现则数组里的对应的元素会变成比0小的数
15.三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]] 解释: nums[0] +
nums[1] + nums[2] = (-1) + 0 + 1 = 0 。 nums[1] + nums[2] + nums[4] = 0
- 1 + (-1) = 0 。 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。 注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入:nums = [0,1,1] 输出:[] 解释:唯一可能的三元组和不为 0 。
示例 3:
输入:nums = [0,0,0] 输出:[[0,0,0]] 解释:唯一可能的三元组和为 0 。
提示:
3 <= nums.length <= 3000
-105 <= nums[i] <= 105
做题思路
1、一开始想的是用哈希解法,两层for循环就可以确定 a 和b 的数值了,可以使用哈希法来确定 0-(a+b) 是否在数组里出现过,但题目要求不可以包含重复的三元组,而若是把符合条件的三元组放进数组中在进行去重,则会过于复杂
2、使用双指针,这样就有利于我们不会取到重复的数值
解题
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list = new ArrayList<List<Integer>>();
//使用双指针必须是有序的
Arrays.sort(nums);
//将i值看作第一个数,left和right分别看出第二个和第三个
for(int i = 0;i < nums.length;i++){
//剪枝:由于前面已排好序,因此若是第一个元素大于0,后面的元素就都大于0
if(nums[i] > 0){
return list;
}
//去重,当前值与上一个值比较,i>0
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
int left = i+1;
int right = nums.length-1;
//不能是left <= right的原因,若是相等,则元素重复,不符合题意
while(left<right){
if(nums[i] + nums[left] + nums[right] == 0){
list.add(Arrays.asList(nums[i],nums[left],nums[right]));
//去重
while(right > left && nums[left] == nums[left+1]){
left++;
}
while(right > left && nums[right] == nums[right-1]){
right--;
}
right--;
left++;
}else if(nums[i] + nums[left] + nums[right] > 0){
right--;
}else{
left++;
}
}
}
return list;
}
}
解答代码存在的疑问
1、对第一个元素第一次去重的理解:
2、对第二、三个元素第二次去重的理解:
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
你可以按 任意顺序 返回答案 。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]示例 2:
输入:nums = [2,2,2,2,2], target = 8 输出:[[2,2,2,2]]
提示:
1 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109
做题思路
与上述三数之和解法一致,但要注意是四个数字,有两次循环,以及题目给定了target值,因此剪枝的处理和上述不同
解题
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
ArrayList<List<Integer>> list = new ArrayList<>();
Arrays.sort(list);
for(int i = 0;i < nums.length;i++){
//剪枝处理:由于是给定的target,并不知晓是负数还是正数,因此要判断第一个元素大于0而直接返回链表,则要保证第一个元素在大于0的前提下还要大于target
if(nums[i] > 0 && nums[i] > target) {
return list;
}
//去重
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
for(int j = i+1; j < nums.length;j++){
//去重
if(j > i+1 && nums[j] == nums[j-1]){
continue;
}
int left = j+1;
int right = nums.length - 1;
while(right>left){
if(nums[i]+nums[j]+nums[left]+nums[right] == target){
list.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
while(left < right && nums[left] == nums[left+1]){
left++;
}
while(left < right && nums[left] == nums[left+1]){
right--;
}
left++;
right--;
}else if(nums[i]+nums[j]+nums[left]+nums[right] < target){
left++;
}else if(nums[i]+nums[j]+nums[left]+nums[right] > target){
right--;
}
}
}
}
return list;
}
}
解答代码存在的疑问
1、剪枝为什么要存在nums[i] > 0 的情况
这保证整条链表都是正数,且第一个就可以与target进行比较,若存在负数,这无法比较
复习
454.四数相加||
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int result = 0
HashMap<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,map.get(temp)+1);
}
map.put(temp,1);
}
}
for(int k = 0;k < nums3.length;k++){
for(int l = 0;l < nums4.length;l++){
int cur = nums3[k] + nums4[l];
if(map.containsKey(0-cur)){
result += map.get(0-cur);
}
}
}
return result;
}
}
383.赎金信
按字母异位词的思路解题
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int result[] = new int[26];
for(int i = 0;i < ransomNote.length();i++){
result[ransomNote.charAt(i) - 'a']++;
}
for(int j = 0;j < magazine.length();j++){
result[magazine.charAt(j) - 'a']--;
}
for(int a : result){
if(a > 0){
return false;
}
}
return true;
}
}
15.三数之和
考虑用set 但要求返回list
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Set<Integer> set = new Set<Integer>();
List<Set<Integer>> list = new ArrayList<>();
HashMap<Integer,Integer> map =new HashMap<>();
for(int k = 0; k < nums.length;k++){
map.put(nums[k],k);
}
for(int i:nums){
for(int j:nums){
int temp = i + j;
if(map.containsKey(0-temp)){
set.add(i);
set.add(j);
set.add(0-temp);
list.add(set);
}
}
}
}
}
矫正
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list = new ArrayList<List<Integer>>();
//使用双指针必须是有序的
Arrays.sort(nums);
//将i值看作第一个数,left和right分别看出第二个和第三个
for(int i = 0;i < nums.length;i++){
//剪枝:由于前面已排好序,因此若是第一个元素大于0,后面的元素就都大于0
if(nums[i] > 0){
return list;
}
//去重,当前值与上一个值比较,i>0
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
int left = i+1;
int right = nums.length-1;
//不能是left <= right的原因,若是相等,则元素重复,不符合题意
while(left<right){
if(nums[i] + nums[left] + nums[right] == 0){
list.add(Arrays.asList(nums[i],nums[left],nums[right]));
//去重
while(right > left && nums[left] == nums[left+1]){
left++;
}
while(right > left && nums[right] == nums[right-1]){
right--;
}
right--;
left++;
}else if(nums[i] + nums[left] + nums[right] > 0){
right--;
}else{
left++;
}
}
}
return list;
}
}
18.四数之和
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
ArrayList<List<Integer>> list = new ArrayList<>();
Arrays.sort(list);
for(int i = 0;i < nums.length;i++){
//剪枝处理:由于是给定的target,并不知晓是负数还是正数,因此要判断第一个元素大于0而直接返回链表,则要保证第一个元素在大于0的前提下还要大于target
if(nums[i] > 0 && nums[i] > target) {
return list;
}
//去重
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
for(int j = i+1; j < nums.length;j++){
//去重
if(j > i+1 && nums[j] == nums[j-1]){
continue;
}
int left = j+1;
int right = nums.length - 1;
while(right>left){
if(nums[i]+nums[j]+nums[left]+nums[right] == target){
list.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
while(left < right && nums[left] == nums[left+1]){
left++;
}
while(left < right && nums[left] == nums[left+1]){
right--;
}
left++;
right--;
}else if(nums[i]+nums[j]+nums[left]+nums[right] < target){
left++;
}else if(nums[i]+nums[j]+nums[left]+nums[right] > target){
right--;
}
}
}
}
return list;
}
}