题目一 1. 两数之和 - 力扣(LeetCode)
思路
二刷这道题有印象,是用hashmap存储target-nums[i]和下标,然后寻找nums[j]在hashmap中,尝试写一下代码:
代码
问题1:怎么取出hashmap的键和值 问题2:怎么保证不出现2+2等于4的情况(这里的2为同一个元素)
class Solution {
public int[] twoSum(int[] nums, int target) {
Hashmap<Integer> record = new Hashmap<>();
for(int i = 0;i < nums.length;i++) {
record.add(target - nums[i],i);
}
for(int i = 0;i < nums.length;i++) {
record.contains(nums[i]);
}
}
}
改动后:
- 去重在这里是通过一个指针i实现的
class Solution {
public int[] twoSum(int[] nums, int target) {
// 哈希表record
Map<Integer,Integer> record = new HashMap<>();
// 结果数组result
int[] result = new int[2];
for(int i = 0;i < nums.length;i++) {
//一定要先判断再改动record
if(record.containsKey(nums[i])) {
result[0] = record.get(nums[i]);
result[1] = i;
}
// 记录互补数字和下标
record.put(target - nums[i],i);
}
return result;
}
}
知识点
HashMap存储键值,键是唯一标识,值可以更新
containsKey(nums[i]):HashMap判断键是否存在的方法get(nums[i]):HashMap获取值的方法put(target - nums[i],i):HashMap添加键值对的方法
题目二454. 四数相加 II - 力扣(LeetCode)
思路
二刷,两个hashmap,一个哈希表存储前两个的和,另一个哈希表存储后两个的和,搜索两个hashmap,看看有无相反数。
- 问题1:哈希表的键是两个数字之和,值是下标数组,但是键可能对应多组下标
解答1:哈希表的键是两个数字之和,值是组合数,如nums1和nums2中有两组{i,j}的和为40,则(40,2) - 问题2:怎样保证{i,j}或{k,l}组合不会重复
解答2:双重for循环遍历 - 问题3:怎样计算总元组数{i,j,k,l}
解答3:若nums1和nums2中有两组{i,j}的和为40,则(40,2),nums3和nums4中有3组{k,l}的和为-40,则(-40,3);由此result_num += 2*3
代码
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer,Integer> hashmap1 = new HashMap<>();
// Map<Integer,Integer> hashmap2 = new HashMap<>();
int result_cnt = 0;
for(int i = 0;i<nums1.length;i++) {
for(int j = 0;j<nums2.length;j++) {
int sum1 = nums1[i] + nums2[j];
if(hashmap1.containsKey(sum1)){
hashmap1.put(sum1,hashmap1.get(sum1)+1);
}else {
hashmap1.put(sum1,1);
}
}
}
// 不用两个hashmap,只需要一个就可以
for(int i = 0;i<nums3.length;i++) {
for(int j = 0;j<nums4.length;j++) {
int sum2 = -nums3[i]-nums4[j];
if(hashmap1.containsKey(sum2)){
result_cnt += hashmap1.get(sum2);
}
}
}
return result_cnt;
}
}
改进代码:
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer, Integer> map = new HashMap<>();
int result = 0;
int count = 0;
//统计两个数组中的元素之和,同时统计出现的次数,放入map
for(int i : nums1){
for(int j : nums2){
result = i + j;
if(map.containsKey(result)){
map.put(result,map.get(result)+1);
}else{
map.put(result,1);
}
}
}
//统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数
for(int i : nums3){
for(int j : nums4){
int temp = 0-(i + j);
if(map.containsKey(temp)){
count += map.get(temp);
}
}
}
return count;
}
}
知识点:
- 双重循环遍历求和
for(int i : nums1){
for(int j : nums2){
result = i + j;
}
}
题目三15. 三数之和 - 力扣(LeetCode)
思路
题目要求返回不重复的三元组,和题目二类似,哈希法
- 哈希表键值是什么?
答:键是前两个数字的和nums[i] + nums[j],值是数组[i,j] - 如何判断是否满足条件?
答:遍历map,寻找键为-nums[k]的数 - 怎么保证i,j,k互不相等?
答:不太清楚,先尝试写一下,再看一下题解
去重问题
尝试之后发现哈希法思路很清晰,但去重操作让代码变得很复杂
所以,这道题目可以换一个思路,用双指针法来解决
双指针法
- 先对数组排序,升序
Arrays.sort(nums); - 定义i、left、right,初始化为0,1,len-1
- 外层循环遍历i
- 内层循环固定i,移动left、right使得三数之和等于0
注意:由于排序之后的数组有隐形条件,使得左右指针的移动有规则,因此这里降低了去重的难度
代码
二维数组result存储符合条件的三元组,用数组[a,b,c]表示
对于a,b,c来说,去重逻辑是不一样的,举两个例子就不难理解了
| nums(排序后) | 去重 | 方法 |
|---|---|---|
[-1,-1,2,7] | / | i指向第一个-1时,不需要去重,否则永远不存在结果[-1,-1,2] |
[-1,-1,0,1] | a | i指向第二个-1时,才需要去重;即nums[i] == nums[i-1] |
[-1,0,0,0,1,1] | b | nums[right] == nums[right-1],右指针需要左移right--去重 |
| c | nums[left] == nums[left+1],左指针需要右移left++去重 |
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> result = new ArrayList<>();
// [-1,-1,0,1]怎么去重
for(int i = 0;i<nums.length-2;i++) {
if(nums[i] > 0) return result;
if(i>0 && nums[i] == nums[i-1]){
// 在下一轮for循环会i+1
continue;
}
int left = i+1;
int right = nums.length - 1;
while(left < right) {
// System.out.println("1");
if(nums[i] + nums[left] + nums[right] == 0) {
result.add(Arrays.asList(nums[i],nums[left],nums[right]));
//[-1,0,0,0,1,1,1]
// b c 如何去重
while(right > left && nums[right] == nums[right-1]) right--;
while(right > left && nums[left] == nums[left+1]) left++;
// continue;
// 去重之后要移动左指针或右指针,否则左右指针一直停留在满足条件的位置
// 形成死循环
right--;
left++;
}else if(nums[i] + nums[left] + nums[right] > 0) {
// 移动右指针
right--;
// continue;
}else{
// 移动左指针
left++;
// continue;
}
}
}
return result;
}
}
题目四18. 四数之和 - 力扣(LeetCode)
思路
排序后,四个指针移动
i,j双重循环固定,left,right双指针
最大的问题还是去重,举几个例子来想一想:
【2,2,2,2,2】i和i-1比较是否相等
【2,2,3,3,3,4,4】-->【2,2,4,4】j也是和j-1比较
感觉不太对劲,应该是(i,j)组成 的组合去重,试一下
代码
- i去重,
if(i > 0 && nums[i] == nums[i-1]) - j去重,
if(j > i+1 && nums[j] == nums[j-1]) - 双指针去重逻辑和三数之和相同
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<>();
if(nums.length < 4) return result;
Arrays.sort(nums);
for(int i = 0;i<nums.length - 3;i++){
// i剪枝
// i去重
if(i > 0 && nums[i] == nums[i-1]) continue;
for(int j = i+1;j< nums.length - 2;j++){
// j剪枝
// j去重
if(j > i+1 && nums[j] == nums[j-1]) continue;
int left = j+1;
int right = nums.length - 1;
while(left < right) {
if(nums[i] + nums[j] + nums[left] + nums[right] > target) {
right--;
}else if(nums[i] + nums[j] + nums[left] + nums[right] < target) {
left++;
}else {
result.add(Arrays.asList(nums[i],nums[j],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;
}
}
- 错误原因:整形Integer溢出了
- 解决办法:加一个剪枝操作