具体代码实现
454.四数相加II
题目具体位置:454.四数相加II
经典题目使用哈希表,用空间换时间。如果只是使用暴力解法的话,时间复杂度为n的4次方。时间复杂度太大。
暴力解法如下(当然是过不了的)。
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
int len=nums1.length;
int count=0;
for(int i=0;i<len;i++){
for(int j=0;j<len;j++){
for(int k=0;k<len;k++){
for(int l=0;l<len;l++){
if(nums1[i]+nums2[j]+nums3[k]+nums4[l]==0)
count++;
}
}
}
}
return count;
}
}
引入哈希表!!!
-
此题不用考虑去重元素数值,最终只需要返回次数。
-
需要遍历的数组共计四个、为nums1到nums4。将其两两拆分,nums1、nums2组成一对,nums3~nums4组成一对。分别进行遍历。 则时间复杂度是2*n^2
-
使用map将nums1、nums2中元素配对所有出现的和a+b进行记录,并记录出现了几次——完全有可能出现和相同的情况。
-
遍历nums3、nums4中元素相加的情况c+d,一旦出现了c+d==-(a+b)的情况出现,count+=map.get(a+b); —— 即count加上a+b的和出现的次数。
-
具体代码:
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
//使用map,键为nums1、nums2数组元素相加的和,值为其出现的次数
Map<Integer,Integer> map=new HashMap<>();
//count记录nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0的次数
int count=0;
//遍历num1、nums2,记录
int len=nums1.length;
for(int i=0;i<len;i++){
//int sum1=0;
for(int j=0;j<len;j++){
//判断当前的和是否之前已经记录在map中
//如果已经存在,则+1
if(map.containsKey(nums1[i]+nums2[j])){
int temp=map.get(nums1[i]+nums2[j])+1;
map.put(nums1[i]+nums2[j],temp);
}
//如果此前没有存入,则次数为1
else{
map.put(nums1[i]+nums2[j],1);
}
}
}
//遍历nums3、nums4数组
for(int k=0;k<len;k++){
for(int l=0;l<len;l++){
//sum2记录num3、num4数组的和
int sum2=nums3[k]+nums4[l];
// count加上-sum2出现在map中的次数。 其实就是为了a+b+sum2==0
if(map.containsKey(-sum2)){
count+=map.get(-sum2);
}
}
}
return count;
}
}
383. 赎金信
题目具体位置
思路:
判断 ransomNote 能不能由 magazine 里面的字符构成,就是看magazine中各字母出现的次数是否大于等于ransomNote中各字母出现的次数。
-
利用哈希表record,记录magazine中各字母出现的次数
-
遍历ransomNote,每取到ransomNote中一个字母,record中对应位置的次数减一。
-
一旦次数不够,则return false—— 无法构成。
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
//简单判断,magazine的长度小于ransomNote长度,肯定无法构成
if(magazine.length()<ransomNote.length()){
return false;
}
//使用数组来构成哈希表 记录下magazine中各个字母出现的次数
int[] record=new int[26];
for(int i=0;i<magazine.length();i++){
//index实际上对应每个'字母' index对应的数值就是出现的次数
int index=magazine.charAt(i)-'a';
record[index]++;
}
//遍历ransomNote字符串
for(int j=0;j<ransomNote.length();j++){
//计算下标 ransomNote.charAt(j)-'a' 然后record对应位置数值减1
record[ransomNote.charAt(j)-'a']--;
//一旦数值“不够”,则无法构成,返回false
if(record[ransomNote.charAt(j)-'a']<0)
return false;
}
return true;
}
}
15. 三数之和
题目地址:15.三数之和
惭愧,完全没有思路。这里学习卡哥思路,用于学习记录。
思路:
- 题目:在给定数组中选取三个数构成三元组——三数之和为0 、三元组不重复(即不同的三元组之间,元素不能完全相同)
具体代码:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result=new ArrayList<>();
int len=nums.length;
//将nums数组升序排列
Arrays.sort(nums);
//升序排列之后如果第一个元素都大于0,那么一定找不到符合条件的三元组
if(nums[0]>0)
return result;
for(int i=0;i<len;i++){
//对a——即nums[i] 剪枝
if(i>0 && nums[i]== nums[i-1])
continue;
int left=i+1;
int right=len-1;
//双指针,一次性找到当前a所对应的所有符合条件的b、c
while(left<right){
int sum=nums[i]+nums[left]+nums[right];
//sum>0证明数字过大,缩小——即right--
if(sum>0){
right--;
}
//sum<0 证明数字过小,增大—— 即left++
else if(sum<0){
left++;
}
else{
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
//很重要! 剪枝
while(left<right && nums[right-1]==nums[right]){right--;}
while(left<right && nums[left+1]==nums[left]){left++;}
right--;
left++;
}
}
}
return result;
}
}
18. 四数之和
实际上跟15.三数之和相似,不同点在于:
-
三数之和a+b+c=0 ,控制住a,然后双指针法来找b和c (期间完成各种剪枝操作)
-
四数之和a+b+c+d=target,就是多加一层循环,通过先控制住a、b,然后再双指针来找到c、d (期间也要对应地完成各种剪枝操作)
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++) {
// nums[i] > target 直接返回, 剪枝操作
if (nums[i] > 0 && nums[i] > target) {
return result;
}
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;
}
}