第454题.四数相加II
这个题目是找出有多少个元组能够满足其值之和为0 。 没有限定顺序,但是从数组中找到的都算。
可以考虑2数之和的逻辑。 key: 我们要找的数组和, value是对应的索引下标,
我们把数组拆成2个,ab为一组,cd为一组。这样的话就可以实现类同:两数之和的逻辑了。
我们首先完成ab数组的所有组合遍历。获得sum值,设计一个map,把不同的sum值都存进去。如果一个sum值多次出现,就对应的value上++,然后再对cd进行for循环 ,对cd进行for循环的过程中,我们要用0减去这次cd中i+j的值,看结果是否在之前的map中的key中,如果找到,我们就计数,最后做累加的过程。
java代码如下:
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer,Integer> mapSum = new HashMap<Integer,Integer>();
int res =0;
for(int i=0;i<nums1.length;i++){
for(int j=0;j<nums2.length;j++){
int sum =nums1[i]+nums2[j];
mapSum.put(sum,mapSum.getOrDefault(sum,0)+1);
}
}
for(int i=0;i<nums3.length;i++){
for(int j=0; j<nums4.length;j++){
res += mapSum.getOrDefault(0-nums3[i]-nums4[j],0);
}
}
return res;
}
}
c++代码如下:
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int ,int> umap ;
for(int i: nums1){
for(int j: nums2){
umap[i+j]++;
}
}
int count =0;
for(int c: nums3){
for(int d:nums4){
if(umap.find(0-(c+d)) != umap.end()){
count += umap[0-(c+d)];
}
}
}
return count;
}
};
python代码如下:
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
hashMap =dict()
for a in nums1:
for b in nums2:
hashMap[a+b]=hashMap.get(a+b,0)+1
count=0
for c in nums3:
for d in nums4:
count +=hashMap.get(0-(c+d),0)
return count
383.赎金信
这个题目是考虑使用magzine中的元素去覆盖ransomNote的元素。 我们便利2个字符串,如果ransomNote的哪一个字符是出自于magzine中的话,我们就对ransomNote元素进行删减操作。
当把magzine遍历完,并在ransomeNote中判断没有对应的元素后,for循环结束。我们再一次的判断ransomNote是不是有元素,如果有元素,说明之前的erase删除操作没有把ransonNote中的元素完整的删除掉。 那么表明ransomNote的元素不完全是使用magzine中的元素的。
2层for循环,这个时间复杂度有点高了。
c++代码如下:
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
for(int i=0; i < magazine.length(); i++){
for(int j=0;j<ransomNote.length(); j++){
if(ransomNote[j]==magazine[i]){
ransomNote.erase(ransomNote.begin()+j);
break;
}
}
}
if(ransomNote.length()==0){
return true;
}
return false;
}
};
通过数组模拟哈希的方式,将magazine的26个小写字母映射成ASCII码值,并且作为索引,对应的字母出现次数作为对应索引下的value值。 然后在ransomNote中进行同样的映射,并做减法,如果最后的这个数组record的各个元素值为小于0 ,说明ransomNote调用的字母多于magazine,那么返回false。如果大于等于0,说明ransomNote的每个字母都来自于magazine(大于0时候,ransomNote的字母是magazine的子集)
代码如下:
bool canConstruct(string ransomNote, string magazine) {
int record[26] = {0}; // 假设只有小写字母a-z
for(int i = 0; i < magazine.length(); i++) {
record[magazine[i] - 'a']++; // 计数 magazine 中每个字符的出现次数
}
for(int j = 0; j < ransomNote.length(); j++) {
int idx =ransomNote[j]-'a';
record[idx]--;
if(record[idx]<0){
return false;
}
}
return true;
}
或者更加清晰,代码冗长一点
//使用一个数组的方式模拟hash来进行处理
bool canConstruct(string ransomNote, string magazine) {
int record [26]= {0};
for(int i=0; i<magazine.length();i++){
record[magazine[i]-'a']++;
}
for(int j=0;j<ransomNote.length();j++){
record[ransomNote[j]-'a'] --;
}
for(int s= 0; s<sizeof(record)/sizeof(record[0]);s++){
if(record[s] <0){
return false;
}
}
return true ;
}
java代码如下:
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
int [] record =new int[26];
for(int i =0; i<magazine.length();i++){
char ch =magazine.charAt(i);
record[ch-'a'] ++;
}
for(int i =0; i<ransomNote.length();i++){
char ch =ransomNote.charAt(i);
record[ch-'a']--;
}
for (int i=0; i<record.length;i++){
if(record[i]<0){
return false;
}
}
return true ;
}
}
或者再简单点
public boolean canConstruct(String ransomNote, String magazine) {
int[] record = new int[26];
for(int i=0; i<magazine.length();i++){
record[magazine.charAt(i)-'a']++;
}
for(int i=0;i<ransomNote.length();i++){
int idx = ransomNote.charAt(i) - 'a';
int j = --record[idx];
//int res=--j;
if(j <0){
return false;
}
}
return true;
}
python代码如下:
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
list =[0 ]*26
for i in magazine:
char = ord(i)-ord('a')
list[char]+= 1
for i in ransomNote :
char =ord(i)-ord('a')
list[char] -=1
for i in list :
if i<0:
return False
return True
from collections import defaultdict
def canConstruct(self,ransomNote:str ,magazine:str)-> bool :
ransomNote_count =[0]*26
magazine_count =[0]*26
for c in magazine:
magazine_count[ord(c)-ord('a')] += 1
for c in ransomNote:
ransomNote_count [ord(c)-ord('a')] +=1
return all(ransomNote_count[i]<= magazine_count[i] for i in range(26))
def canConstruct(self ,ransomNote :str ,magazine :str)-> bool:
hashmap =defaultdict(int)
for c in magazine:
hashmap[c] +=1
for x in ransomNote :
value =hashmap.get(x)
if not value :
return False
else:
hashmap[x] -=1
return True
def conConstruct6 (self,ransomNote:str,magazine:str )-> bool:
return all ( ransomNote(c)< magazine(c) for c in set(ransomNote))
#觉得这个有问题,回头研究
def conConstruct (self,ransomNote:str,magazine:str )-> bool:
# 统计 ransomNote 和 magazine 中每个字符的出现次数
ransomNote_count = Counter(ransomNote)
magazine_count = Counter(magazine)
# 检查 ransomNote 中每个字符的数量是否 <= magazine 中对应字符的数量
for char, count in ransomNote_count.items():
if magazine_count[char] < count:
return False
return True
15. 三数之和
这道题目使用双指针的方式进行处理。 从第一位置算起,i,left,right, 我们针对每个i(for循环nums的size() ),
首先进行兼枝,如果是第一位就大于0的情况,break, 有重复的i,我们要去重。
如果 nums[i] + nums[left] + nums[right] 三者的和大于零,我们right--,如果小于0,我们left++, 这样就能找到nums[i] + nums[left] + nums[right]==0的位置,
然后把这个存到二维动态列表vector中去。 拿到结果后,我们要进行判断,如果nums[left]和它下一位重复,我们要进行去重,同样的,右侧right一样要进行去重。
之后我们还需要做一步工作,再一次内缩指针。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++) {
if (nums[i] > 0) {
break;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum > 0) {
right--;
} else if (sum< 0) {
left++;
} else {
result.push_back({nums[i], nums[left], nums[right]});
while (left<right && nums[left] == nums[left + 1]) {
left++;
}
while ( left<right && nums[right] == nums[right - 1]) {
right--;
}
left++;
right--;
}
}
}
return result;
}
};
java代码如下:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
// 定义一个二维list,用来存放不同的一维list,每个一维list中是3个元素,能够满足nums[i] + nums[j] + nums[k] == 0
List<List<Integer>> result = new ArrayList<>();
// 开始对这个nums数组排序进行遍历循环,并对其中的一些逻辑进行过滤,比如说第一个值就是大于0,比如第一个值和它之后的挨着的值是同一个值,这种情况都要进行过滤。
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
break;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.length - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum > 0) {
right--;
} else if (sum < 0) {
left++;
} else {
// 创建一个新的一维数组list
List<Integer> triplet = new ArrayList<>();
triplet.add(nums[i]);
triplet.add(nums[left]);
triplet.add(nums[right]);
result.add(triplet);
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
left++;
right--;
}
}
}
return result ;
}
}
python :
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = []
nums.sort() # 排序,便于后续的双指针法
for i in range(len(nums)):
if nums[i] > 0:
return result # 如果当前数大于0,后续不可能有满足条件的三元组了
if i > 0 and nums[i] == nums[i - 1]:
continue # 跳过重复的元素
left = i + 1
right = len(nums) - 1
while left < right:
sum = nums[i] + nums[left] + nums[right]
if sum > 0:
right -= 1
elif sum < 0:
left += 1
else:
result.append([nums[i], nums[left], nums[right]])
# 跳过重复的数字
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
# 更新指针以继续寻找新的三元组
left += 1
right -= 1
return result
有人写出来很der的代码,我没有看懂,帖上来,有脑子的人帮我看看,这个der为何要这么写:
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = []
nums.sort()
# 找出a + b + c = 0
# a = nums[i], b = nums[j], c = -(a + b)
for i in range(len(nums)):
# 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
if nums[i] > 0:
break
if i > 0 and nums[i] == nums[i - 1]: #三元组元素a去重
continue
d = {}
for j in range(i + 1, len(nums)):
if j > i + 2 and nums[j] == nums[j-1] == nums[j-2]: # 三元组元素b去重
continue
c = 0 - (nums[i] + nums[j])
if c in d:
result.append([nums[i], nums[j], c])
d.pop(c) # 三元组元素c去重
else:
d[nums[j]] = j
return result
18 四数之和
这个题目和三数字之和的解题思路是差不多的,需要注意的是这个4数字之和,我们可以多出来一个固定的数k, 然后再遍历i=k+1, i之后的思路left ,right 和之前的三数字之和是一样的。
我们剪枝的时候, 第一层是对k的循环,这个过程中,我们过滤是考虑nums[k]>0&&target>0,如果target是负数,那就中招了。
同样的道理,我们在对i进行遍历循环时候,首先要把nums[k] +nums[i] 看成是一个整体, 其次我们剪枝时候,也要把nums[k] +nums[i]>0 && target>0 考虑进去。
其他的思路和之前的三数之和逻辑一样。
代码如下:
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
// 类似3个数和的操作,进行一个遍历,多了一个固定的k
List<List<Integer>> result = new ArrayList<>();
for (int k = 0; k < nums.length; k++) {
// 进行剪枝策略,
// nums[k] > 0 && nums[k] > target && target > 0
// nums[k] > target && target > 0
if (nums[k] > target && nums[k] > 0) {
return result;
}
if (k > 0 && nums[k] == nums[k - 1]) {
continue;
}
// 想一下i怎么进行固定呢?对i进行for循环,
for (int i = k + 1; i < nums.length; i++) {
// k和i 2个看成是一个整体
if (nums[i] + nums[k] > target && (nums[i] + nums[k]) > 0 ) {
continue;
}
if (i > k + 1 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.length - 1;
while (left < right) {
int sum = nums[k] + nums[i] + nums[left] + nums[right];
if (sum > target) {
right--;
} else if (sum < target) {
left++;
} else {
result.add(Arrays.asList(nums[k] ,nums[i] , nums[left] , nums[right]));
while (left<right && nums[left] == nums[left + 1]) {
left++;
}
while (left<right && nums[right] == nums[right - 1]) {
right--;
}
left++;
right--;
}
}
}
}
return result;
}
}