赞
踩
今天是哈希表训练的第二天,哈希表相关的基础知识在昨天已经总结过了,看到今天的四道题目感觉还是比较有挑战的,那就加油吧!
中等
看到题目就想到了要使用哈希表,由于统计的是数量,因此就要选择python中的字典或者Java中的HashMap了,这样可以保存键值对。
1. 使用哈希表。
2. 前两个数组的和以及出现的次数作为键值对先存储。
3. 检查后两个数组和的相反数有没有在哈希表中出现过,出现则获取次数。
python
- class Solution:
- def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
- dic = {}
- res = 0
-
- # 遍历前两个数组并记录和以及出现的次数
- for i in nums1:
- for j in nums2:
- snum = i+j
- dic[snum] = dic.get(snum, 0) + 1
-
- # 遍历后两个数组并检查和的相反数有没有出现在dic中, 出现了累加数量
- for i in nums3:
- for j in nums4:
- snum = 0 - (i+j)
- if snum in dic:
- res += dic[snum]
-
- return res
Java
- class Solution {
- public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
- HashMap<Integer, Integer> hash = new HashMap<>();
- int res = 0;
- for(int i: nums1){
- for(int j: nums2){
- int sum = i+j;
- int count = hash.getOrDefault(sum, 0);
- hash.put(sum, count+1);
- }
- }
-
- for(int i: nums3){
- for(int j: nums4){
- int sum = 0 - (i+j);
- if(hash.containsKey(sum)){
- res += hash.get(sum);
- }
- }
- }
-
- return res;
- }
- }
简单
看到题目的第一想法是要使用哈希表,由于要保存字符及数量因此要用字典或者HashMap,不同点是两个数组的大小不一定相等。
python
- class Solution:
- def canConstruct(self, ransomNote: str, magazine: str) -> bool:
- if len(magazine) < len(ransomNote):
- return False
-
- dic = {}
- for m in magazine:
- dic[m] = dic.get(m, 0) + 1
-
- for r in ransomNote:
- if r not in dic:
- return False
- dic[r] -= 1
- if dic[r] < 0:
- return False
- return True
Java
- class Solution {
- public boolean canConstruct(String ransomNote, String magazine) {
- if(magazine.length() < ransomNote.length()){
- return false;
- }
- HashMap<Character, Integer> hash = new HashMap<>();
- for(int i=0; i < magazine.length(); i++){
- int count = hash.getOrDefault(magazine.charAt(i), 0) + 1;
- hash.put(magazine.charAt(i), count);
- }
-
- for(int i=0; i < ransomNote.length(); i++){
- if(!hash.containsKey(ransomNote.charAt(i))){
- return false;
- }
-
- int count = hash.get(ransomNote.charAt(i)) - 1;
- if(count < 0){
- return false;
- }else{
- hash.put(ransomNote.charAt(i), count);
- }
- }
- return true;
- }
- }
1. 解题思路和有效的字母异位词解法类似,使用数组(由于字符是有范围的,所以可以定义一个常量大小的数组,很大程度降低了空间复杂度)。
python
- class Solution:
- def canConstruct(self, ransomNote: str, magazine: str) -> bool:
- if len(magazine) < len(ransomNote):
- return False
-
- count = [0] * 26
- for i in magazine:
- index = ord(i) - ord('a')
- count[index] += 1
-
- for i in ransomNote:
- index = ord(i) - ord('a')
- if count[index] < 1:
- return False
-
- count[index] -= 1
- return True
Java
- class Solution {
- public boolean canConstruct(String ransomNote, String magazine) {
- if(magazine.length() < ransomNote.length()){
- return false;
- }
- int[] count = new int[26];
- for(int i=0; i < magazine.length(); i++){
- int index = magazine.charAt(i) - 'a';
- count[index] += 1;
- }
-
- for(int i = 0; i < ransomNote.length(); i++){
- int index = ransomNote.charAt(i) - 'a';
- if(count[index] < 1){
- return false;
- }else{
- count[index] -= 1;
- }
- }
- return true;
- }
- }
简单
没什么好的解题思路,想用哈希表,但是也没有梳理好去重的思路。
1. 先对数组排序。
2. 使用双指针法寻找当前和nums[i] 和为0的组合。
3. 注意去重:1)对nums[i]去重;2)当找到满足和为0的三个数以后对下一个nums[left]和nums[right去重]。
python
- class Solution:
- def threeSum(self, nums: List[int]) -> List[List[int]]:
- res = []
- nums.sort() # 排序
-
- for i in range(len(nums)):
- # 剪枝 如果nums[i] > 0 则就结束循环
- if nums[i] > 0:
- break
-
- # 对num[i]去重, 当前值与上次循环的值一样了,则当前循环就不再走下去了,进入下一循环
- if(i>0 and nums[i] == nums[i-1]):
- continue
-
- # 设置left right的起始位置
- left = i+1
- right = len(nums) - 1
-
- # 查找与当前nums[i]和为0的nums[left]和nums[right]
- while(left < right):
- snum = nums[i] + nums[left] + nums[right]
- if snum > 0:
- right -= 1
- elif snum < 0:
- left += 1
- else:
- res.append([nums[i], nums[left], nums[right]])
- left += 1
- right -= 1
- # 注意这里需要对下一个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
- return res
Java
- class Solution {
- public List<List<Integer>> threeSum(int[] nums) {
- List<List<Integer>> resList = new ArrayList<>();
- 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{
- resList.add(Arrays.asList(nums[i], nums[left],nums[right])); //Arrays.asList将数组转换成list
- left++;
- right--;
-
- while(left < right && nums[left] == nums[left-1]){
- left++;
- }
- while(left < right && nums[right] == nums[right+1]){
- right--;
- }
- }
- }
-
- }
- return resList;
- }
- }
中等
看道这个题目的时候,结合上一道题目,想到的使用双指针法,但是没能梳理出来比较好的思路。
1. 使用指针法
2. 和三个数的和解法类型,只是增加了一层循环,同样需要注意去重。
python
- class Solution:
- def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
- res = []
- nums.sort() # 首先还是要先排序
- n = len(nums)
-
- # 两层循环遍历拿到四元组的前两个数据
- for i in range(n):
- if target > 0 and nums[i] > target: # 适当剪枝
- break
-
- if i > 0 and nums[i] == nums[i-1]: # 去重
- continue
-
- for j in range(i+1, n):
- if target > 0 and nums[i] + nums[j] > target: # 剪枝
- break
-
- if j > i+1 and nums[j] == nums[j-1]: # 去重
- continue
-
- # 接下来开始找四元组剩下的两个数了,和三数之和逻辑一样设置left right指针
- left = j+1
- right = n-1
- while left < right:
- snum = nums[i] + nums[j] + nums[left] + nums[right]
- if snum > target:
- right -= 1
- elif snum < target:
- left += 1
- else:
- res.append([nums[i], nums[j], nums[left], nums[right]])
- left += 1 # 找到目标以后,一起移动left和right到下一个位置
- right -= 1
-
- # 接下来就要对 left right去重了
- while left < right and nums[left] == nums[left - 1]:
- left += 1
- while left < right and nums[right] == nums[right + 1]:
- right -= 1
- return res
java
- class Solution {
- public List<List<Integer>> fourSum(int[] nums, int target) {
- List<List<Integer>> res = new ArrayList<>();
- Arrays.sort(nums);
- int n = nums.length;
-
- for(int i=0; i<n; i++){
- if(target > 0 && nums[i] > target){
- break;
- }
-
- if(i > 0 && nums[i] == nums[i-1]){
- continue;
- }
-
- for(int j=i+1; j<n; j++){
- if(target > 0 && nums[i] + nums[j] > target){
- break;
- }
-
- if(j > i+1 && nums[j] == nums[j-1]){
- continue;
- }
-
- int left = j+1;
- int right = n-1;
- while(left < right){
- long sum = (long)nums[i] + nums[j] + nums[left] + nums[right]; // 坑点要用long数据类型,不然会导致超过整型最大值溢出 eg: 测试用例 nums = [1000000000,1000000000,1000000000,1000000000] target = -294967296
- if(sum > target){
- right--;
- }else if(sum < target){
- left++;
- }else{
- res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
- left++;
- right--;
- while(left < right && nums[left] == nums[left-1]){
- left++;
- }
- while(left < right && nums[right] == nums[right+1]){
- right--;
- }
- }
- }
- }
- }
- return res;
- }
- }
感觉又是比较烧脑的一天,但是也有很多收获,在数据量比小且有一定范围的时候,我们可以使用数组,这样空间复杂度就会大大减少;在做三数之和四书之和的时候我们通常使用指针的方法;注意java语言的整型溢出,这是今天踩到的一个坑点。总的来说虽说哈希表的使用比昨天顺手一点了,但是新增的三数之和四数之和的思维还局限在哈希表中,切记思维定式,周末的时候再回顾一下吧。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。