【Leecode】代码随想录哈希篇day7(n数之和(双指针返场))

赎金信

  • 题目链接:数组hash典型
  • 我的解法:看成源字符串和目标字符串都由大小写字母组成了,记录一下
class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int magcnt[] = new int[52];//0~25小写字母,26~51大写字母
        for(int i = 0; i < magazine.length(); i++)
        {
            char c = magazine.charAt(i);
            if(c >= 'a' && c <= 'z') magcnt[(c - 'a')]++;
            else magcnt[(c - 'A' + 26)]++;
        }
        for(int i = 0; i < ransomNote.length(); i++)
        {
            char c = ransomNote.charAt(i);
            if(c >= 'a' && c <= 'z')
            {
                magcnt[(c - 'a')]--;
                if(magcnt[(c - 'a')] < 0) return false;
            }
            else{
                magcnt[(c - 'A' + 26)]--;
                if(magcnt[(c - 'A' + 26)] < 0) return false;
            }
        }
        return true;
    }
}

总结:String类型遍历可以使用str.charAt(i),也可以str.toCharArray()之后用foreach遍历
str.indexOf(c, index)返回字符c从String下标index开始出现的第一个位置,没有返回-1

  • 复杂度:时间复杂度 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)

n数之和

排序-降维至双指针(剪枝&去重)-双指针相向锁定答案(将和与target比较大小)

两数之和

class Solution {
    public int[] twoSum(int[] nums, int target) {
    int m=0,n=0,k,board=0;
    int[] res=new int[2];
    int[] tmp1=new int[nums.length];
    //备份原本下标的nums数组
    System.arraycopy(nums,0,tmp1,0,nums.length);
    //将nums排序
    Arrays.sort(nums);
    //双指针
    for(int i=0,j=nums.length-1;i<j;){
        if(nums[i]+nums[j]<target)
            i++;
        else if(nums[i]+nums[j]>target)
            j--;
        else if(nums[i]+nums[j]==target){
            m=i;
            n=j;
            break;
        }
    }
    //找到nums[m]在tmp1数组中的下标
    for(k=0;k<nums.length;k++){
        if(tmp1[k]==nums[m]){
            res[0]=k;
            break;
        }
    }
    //找到nums[n]在tmp1数组中的下标
    for(int i=0;i<nums.length;i++){
        if(tmp1[i]==nums[n]&&i!=k)
            res[1]=i;
    }
    return res;
}
}

总结:本题记录的二元组由下标组成,所以哈希表实现的方式更简单。

三数之和

  • 题目链接:三数之和
  • 我的解法:看成三元组存的是不重复下标了,还是想降维的思想用不了了,根本没想到去重这事。。。
  • 正确解法:
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        //排序为了之后去重,本题不需记录下标
        Arrays.sort(nums);
        for(int i = 0; i < nums.length; i++) 
        {
            //排序后正整数数组中三数之和不能为0
            if(nums[i] > 0) break;

            //对第一个数去重
            /*使用num[i]!=nums[i-1]防止漏解相同数字不在相同位置如{-1,-1,2}*/
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            //创指针选择第二三个数
            int left = i + 1;
            int right = nums.length - 1;
            while(left < right)
            /*因为不重复,所以有效范围应该是left!=right*/
            {
                //上来不直接去重,因为可能漏解{0,0,0}
                if(nums[i] + nums[left] + nums[right] < 0) left++;
                else if(nums[i] + nums[left] + nums[right] > 0) right--;
                else{
                    res.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    //第二三个数去重,左右落点都在重复的最后一个数上
                    while(left < right && nums[right] == nums[right - 1]) right--;
                    while(left < right && nums[left] == nums[left + 1]) left++;
                    //无论有无重复,都要从不重复的左右开始判断
                    right--;
                    left++;
                }
            }
        }
    return res;
    }
}

总结:不重复包括两层意思:三元组内存储元素对应下标彼此不重复,用一指针定一然后其他指针在后移二解决;还有就是三元组间组成的整体三个数字不重复,通过排序去重实现。

四数之和(四数组版)

  • 题目链接:四数之和
  • 我的解法:转化为两数之和问题,时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( n 2 ) O(n^2) O(n2)
class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        HashMap<Integer, Integer> plus = new HashMap<Integer, Integer>();
        int ans = 0;
        for(int i = 0; i < nums1.length; i++)
            for(int j = 0; j < nums2.length; j++)
            {
                int sum = nums1[i] + nums2[j];
                if(plus.containsKey(sum))
                {
                    plus.replace(sum, (plus.get(sum) + 1));
                }
                else
                {
                    plus.put(sum, 1);
                }
            }
        for(int i = 0; i < nums3.length; i++)
            for(int j = 0; j < nums4.length; j++)
            {
                int sum = nums3[i] + nums4[j];
                if(plus.containsKey((0-sum))) 
                {
                    ans += plus.get((0 - sum));
                }
            }
        return ans;
    }
}

总结:第一个二重循环插入(两数之和,两数之和出现次数)进入hash表时判断是否存在再将次数+1的逻辑可以用一句话
javahashmap.put(sum, hashmap.getOrDefault(sum, 0) + 1);
代替

四数之和(单数组版)

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        for(int i = 0; i < nums.length; i++)
        {
            // 这里不加剪枝后续也会发生溢出,要搭配转为long类型的四元素求和使用
            if (nums[i] > 0 && nums[i] > target) {
                return res;
            }
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            for(int j = i + 1; j < nums.length; j++)
            {
                if(j > (i + 1) && nums[j] == nums[j - 1]) continue;

                int left = j + 1;
                int right = nums.length - 1;
                while(left < right)
                {
                    long sum = (long) (nums[i] + nums[j] + nums[left] + nums[right]);
                    if(sum > target) right--;
                    else if(sum < target) left++;
                    else{
                        res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        while(left < right && nums[right] == nums[right - 1]) right--;
                        while(left < right && nums[left] == nums[left + 1]) left++;
                        right--;
                        left++;
                    }
                }
            }
        }
        return res;
    }
}

总结:n数之和,在记录的是不同n元组相加的元素值时使用双指针更方便,外层是n-2重循环去重前n-2个加入n元组的数,内层使用双指针锁定答案。
注意:随着相加元素的增多,和会溢出,这就要求转为long类型,同时及时剪枝,但由于复杂度是 O ( n n ) O(n^n) O(nn),n不会太大。

  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值