Day 07
哈希表
5. 四数相加2(力扣454)
- 题目描述:四数相加2
- 思路:四层遍历肯定超时,试了一下三层遍历也超时,所以最多只能遍历两次。那就把nums1 nums2可能产生的和记录在一个字典里,注意因为nums1,nums2可能有重复的数字,所以产生一个和的可能性有多种,因此要用字典的键记录可能得和,用字典的值记录和出现的次数。再遍历nums3,nums4,如果这连个数的和的相反数出现在字典里,就把count加上字典的值那么多个可能。
- python语法细节:
- 字典的get用法
dic.get(key,deafault_value)
get方法可以实现,在字典中寻找某个键,如果存在返回这个键对应的值,如果不存在返回默认值。
- python实现:
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
## 三层暴力循环超时
# count = 0
# dic4 = {}
# for i in range(len(nums4)):
# if nums4[i] not in dic4:
# dic4[nums4[i]] = 1
# else:
# dic4[nums4[i]] +=1
# print(dic4)
# for i in nums1:
# for j in nums2:
# for k in nums3:
# if (-i-j-k) in dic4:
# count = count + dic4[-i-j-k]
# return count
### 两层循环 我的写法
# dic1 = {}
# for i in range(len(nums1)):
# for j in range(len(nums2)):
# if nums1[i]+nums2[j] not in dic1:
# dic1[nums1[i]+nums2[j]] = 1
# else:
# dic1[nums1[i]+nums2[j]] += 1
# dic2 = {}
# for i in range(len(nums3)):
# for j in range(len(nums4)):
# if nums3[i]+nums4[j] not in dic2:
# dic2[nums3[i]+nums4[j]] = 1
# else:
# dic2[nums3[i]+nums4[j]] += 1
# print(dic1,dic2)
# count = 0
# for key in dic1:
# if -key in dic2:
# count += dic1[key] *dic2[-key]
# return count
## 两层循环,用get,只建立一个dic
dic = {}
for num1 in nums1:
for num2 in nums2:
dic[num1+num2] = dic.get(num1+num2,0)+1
count = 0
for num3 in nums3:
for num4 in nums4:
if -(num3+num4) in dic:
count = count + dic[-(num3+num4)]
return count
- C++语法细节
- C++中unordered_map用法
- 创建
std::unordered_map<KeyType, ValueType> my_map;
- 插入元素
my_map[key]=value;
my_map[key]++
这种插入方式可以实现当key不存在时,添加一个元素,值设置为0.- 查找元素
std::string value = my_map[key];
- 删除元素
my_map.erase(key)
- 判断元素是否存在
if (my_map.find(key) != my_map.end()
- 获取集合大小:
int size = my_map.size();
- C++实现
#include <unordered_map>
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
std::unordered_map<int,int> umap;
for (int num1:nums1){
for(int num2:nums2){
if (umap.find(num1+num2)==umap.end()) {
umap[num1+num2]=1;
}else{
umap[num1+num2] ++;
}
}
}
int count = 0;
for (int num3:nums3){
for(int num4:nums4){
int sum_ = -(num3+num4);
if (umap.find(sum_)!=umap.end()) {
count += umap[sum_];
}
}
}
return count;
}
};
6. 赎金信(力扣383)
- 题目描述:赎金信
- 思路:把杂志中出现的字母和每个字母出现的字数用字典统计下,再遍历赎金信,用过的字母要减1.
- python语法细节:
- python实现:
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
dic={}
for m in magazine:
dic[m] = dic.get(m,0)+1
print(dic)
for r in ransomNote:
if dic.get(r,0)>=1:
dic[r] -=1
else:
return False
return True
- C++语法细节
- 对字符串进行for each遍历
for(char ch:my_string)
- 构造一个键位字符 值为整数的字典
unordered_map<char,int> umap;
- C++实现
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
unordered_map<char,int> umap;
for(char ch:magazine){
umap[ch]++;
}
for(char ch:ransomNote){
if(umap[ch]>=1){
umap[ch]--;
}
else{
return false;
}
}
return true;
}
};
7. 三数之和(力扣15)
- 题目描述:三数之和
- 思路:这题很难。第一个想法是,遍历两轮,然后剩下的用哈希寻找,但是由于数组有重复,只能用键记录位置,值记录数值。但是这样寻找的会后需要再字典里找值而不是找键,还是要遍历字典。所以这个题需要用双指针。先对数组进行排序,外层依旧是遍历,内层用两个指针分别指向开头和结尾。由于需要对结果去重,所以想到把结果用set去一下重,但是超时了,所以还是要考虑剪枝去重。
- 这题怎么剪枝!
最好自己设计一个重复比较高的测试用例,不然就是一遍一遍的跑,一遍一遍的剪枝,痛苦面具 - 外层剪枝:当外层指针遇到一个刚刚已经遇到一遍的元素,那就continue跳过!但是这里需要有i-1,所以要把其实元素排除
- left剪枝:和外层剪枝类似
- right剪枝:当right指针遇到一个已经遇到过一次的元素,那就continue跳过。但是这里需要有i+1,所以要把末尾元素排除
- python语法细节:
- 在while中移动指针后不做操作,要continue让它去while处判断条件,不然就往下继续执行了
- python实现:
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
### 一次痛苦的尝试
# dic={}
# for num in nums:
# dic[num]=dic.get(num,0)+1
# for key in dic:
# if dic[key]>3:
# dic[key]=3
# numss = []
# for key in dic:
# count = dic[key]
# for i in range(count):
# numss.append(key)
# print(numss)
# numss.sort()
# l = len(numss)
# res = []
# for i in range(l):
# if numss[i]>0:
# return res
# if i!=0 and numss[i] == numss[i-1]:
# # print(f'外循环重复{i}')
# continue
# left = i+1
# right = l-1
# while left < right:
# # print(f'遍历到了什么{i,left,right}')
# if(left!= i+1 and numss[left]==numss[left-1]):
# left +=1
# continue
# if (right!=l-1 and numss[right] == numss[right+1]):
# right -=1
# continue
# if numss[i]+numss[left]+numss[right]==0:
# # print(i,left,right)
# res.append([numss[i],numss[left],numss[right]])
# left += 1
# elif numss[i]+numss[left]+numss[right]<0:
# left += 1
# else:
# right -=1
# return res
### 删减以下前面去重的部分
nums.sort()
l = len(nums)
res = []
for i in range(l):
if nums[i]>0:
return res
if i!=0 and nums[i] == nums[i-1]:
# print(f'外循环重复{i}')
continue
left = i+1
right = l-1
while left < right:
# print(f'遍历到了什么{i,left,right}')
if(left!= i+1 and nums[left]==nums[left-1]):
left +=1
continue
if (right!=l-1 and nums[right] == nums[right+1]):
right -=1
continue
if nums[i]+nums[left]+nums[right]==0:
# print(i,left,right)
res.append([nums[i],nums[left],nums[right]])
left += 1
elif nums[i]+nums[left]+nums[right]<0:
left += 1
else:
right -=1
return res
- C++语法细节
- C++中没有elif 要写if …else if …
- C++中嵌套列表额实现
vector<vector<int>> res;
- 向嵌套列表中增加元素
res.push_back({...}
- C++中对列表排序
sort(nums.begin(),nums.end())
- C++实现
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> res;
for(int i=0;i<nums.size();i++){
if (nums[i]>0){
return res;
}
if(i!=0 and nums[i]==nums[i-1]){
continue;
}
int left = i+1;
int right = nums.size()-1;
while (left<right){
if (left!=i+1 and nums[left]==nums[left-1]){
left++;
continue;
}
if (right!=nums.size()-1 and nums[right]==nums[right+1]){
right--;
continue;
}
int sum_ = nums[i]+nums[left]+nums[right];
if (sum_==0){
res.push_back({nums[i],nums[left],nums[right]});
left++;
}
else if(sum_<0){
left++;
}
else{
right--;
}
}
}
return res;
}
};
8. 四数之和(力扣18)
- 题目描述:四数之和
- 思路:有了上一道三数之和,这道题就好想一些了。就外圈多加一次循环遍历连个数,内圈使用两个指针实现一次遍历两个数字。也就是说,使用双指针可以减少一次遍历,三次变两次,四次变三次,懂伐~
- 这里的剪枝有一丢丢变化,不再是第一个数大于0就跳过,而是第一个数大于target的1/4就跳过,因为数组是顺序的,第一个数应该最小,最多占目标值的1/4。
- python语法细节:
- python实现:
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
result = []
l = len(nums)
nums.sort()
for i in range(l):
if nums[i] >target/4:
return result
if i!=0 and nums[i]==nums[i-1]:
continue
for j in range(i+1,l):
if j!=i+1 and nums[j]==nums[j-1]:
continue
left = j+1
right = l-1
while left<right:
# print(f'遍历到{i,j,left,right}')
if left!= j+1 and nums[left]==nums[left-1]:
left+=1
continue
if right!= l-1 and nums[right]==nums[right+1]:
right-=1
continue
sum = nums[i]+nums[j]+nums[left]+nums[right]
if sum==target:
result.append([nums[i],nums[j],nums[left],nums[right]])
left +=1
elif sum<target:
left +=1
else:
right -= 1
return result
- C++语法细节
- 这里有一个测试用例
[0,0,0,1000000000,1000000000,1000000000,1000000000]
- C风格的数据类型转换
(type) expression
- C++中数据类型转换
int num = 123; long long_num = num;
- C++实现
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
vector<vector<int>> res;
int l = nums.size();
for(int i = 0;i<l-1;i++){
if(nums[i]>target/4){
return res;
}
if(i!=0 and nums[i]==nums[i-1]){
continue;
}
for(int j=i+1;j<l-1;j++){
if(j!=i+1 and nums[j]==nums[j-1]){
continue;
}
int left = j+1;
int right = l-1;
while (left<right){
// cout << i << ", " << j << ", " << left << ", " << right << endl;
long sum_ = (long)nums[i]+nums[j]+nums[left]+nums[right];
if (left!=j+1 and nums[left]==nums[left-1]){
left++;
continue;
}
if (right!=l-1 and nums[right]==nums[right+1]){
right--;
continue;
}
if (sum_ == target){
res.push_back({nums[i],nums[j],nums[left],nums[right]});
left++;
right--;
}
else if(sum_<target){
left++;
}
else{
right--;
}
}
}
}
return res;
}
};