力扣复习启程!!从two sum开始


又是一年秋招季,不复习算法题可不行,力扣复习第一篇文章献给两数之和

两数之和

两数之和,数量比较少,我们知道一个sum,然后我们枚举一个数A,另一个数自然就是sum-B,因此无序解法占用的时间和空间都不算多,如果上升到三个数、四个数,无序数组就不那么好做了,还是使用有序数组解法更加通用

题目:
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标

无序数组

无序数组的力扣地址

暴力双循环不要想了,我们遍历一次数组,能够都所有数字进行一次枚举,每个枚举的值记为A,而目标值为target,那么我们需要找的哪一个值就是target-A。
我们需要使用一个缓存,存储已经枚举过的数,每当我们枚举到一个数需要做出两个处理:
【1】判断这个数对应的target-A是否已经存在,如果存在说明已经找到了,退出
【2】否则,将这数暂存起来

    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer, Integer> temp = new HashMap<>();
        int[] res = new int[2];
        for (int i = 0; i < nums.length; i++) {
            int num=target-nums[i];
            if(temp.containsKey(num)){
                res[0]=temp.get(num);
                res[1]=i;
                break;
            }else {
                //值做键,索引做值
                temp.put(nums[i],i);
            }
        }
        return res;
    }

我们使用map将过去枚举过的数的值与下标存储起来

有序数组

因为数组是有序的,那么元素从左到右,必然是依次变大的,我们维护一对指针,一个指向左侧,一个指向右侧。每轮迭代,我们就将这两个指针划分出的区域缩小一个单位,因此在时间复杂度O(n)下,我们就可以检索出这两个数。

      public int[] twoSum(int[] numbers, int target) {
    int a=0;
    int b=numbers.length-1;
    while (a<b){
        if(numbers[a]+numbers[b]==target)break;
        else if(numbers[a]+numbers[b]<target){
            a++;
        }else b--;
    }
    return new int[]{a+1,b+1};
    }

三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

我们依然按照有序数组两数之和的思路,我们能不能三数之和,通过限定其中一个数来降维作两数之和呢?
我们在外循环维护一个数,然后内循环就可以看作两数之和的解法了

        for(int i=0;i<nums.length-2;i++){
            int first = nums[i]; 
            int j=i+1;
            int k=nums.length-1;

每轮迭代,固定一个first,然后选定一对指针j和k,那么目标值就是-first。
由于题中要求不能出现重复的三元组,因此我们还需要进行去重操作——保证i总是指向相对靠右的那个“重复值”,而j总是指向相对靠左的那个“重复值”
外循环同样需要去重,保证i总是指向靠右的“重复值”

三数之和,最外层循环每进行一轮迭代,都将产生一个三元组。

    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        if(nums.length<3)return res;
        Arrays.sort(nums);
        for(int i=0;i<nums.length-2;i++){
            int first = nums[i];
            int j=i+1;
            int k=nums.length-1;
            while(j<k){
                if(nums[j]+nums[k]==-first){
                    ArrayList<Integer> list = new ArrayList<>();
                    list.add(first);list.add(nums[j]);list.add(nums[k]);
                    res.add(list);
                    while(j+1<k&&nums[j]==nums[j+1])j++;
                    while(j<k-1&&nums[k-1]==nums[k])k--;
                    j++;k--;    //并没有结束,还需要接着找
                }else if(nums[j]+nums[k]>-first){
                    k--;
                }else{
                    j++;
                }
            }
            //结算后去重
            while (i + 1 < num.length - 2 && num[i+1] == num[i]) ++i;
        }
        return res;
    }

注意,外循环不要一开始就i++去重,因为内循环需要会使用到对应的位置,当内循环计算完毕后,保证内循环已经使用过才可以去重。如-2 -2 2 4 0可以由-2 -2 4 和 -2 2 0

四数之和

其实是一个套路,只要都是从2、3之和变式过来的,N数之和也能这么做

    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> res =new ArrayList<>();
        if(nums.length<4)return res;
        Arrays.sort(nums);
        for (int left_1 = 0; left_1 < nums.length-3; left_1++) {
            for (int left_2 = left_1+1; left_2 < nums.length-2; left_2++) {
                int temp=target-nums[left_1]-nums[left_2];
                int left_3=left_2+1;
                int right_3=nums.length-1;
                while (left_3<right_3){
                    if(nums[left_3]+nums[right_3]>temp){
                        right_3--;
                    }else if(nums[left_3]+nums[right_3]<temp){
                        left_3++;
                    }else {
                        ArrayList<Integer> list = new ArrayList<>();
                        list.add(nums[left_1]);
                        list.add(nums[left_2]);
                        list.add(nums[left_3]);
                        list.add(nums[right_3]);
                        res.add(list);
                        while (left_3+1<right_3&&nums[left_3+1]==nums[left_3])left_3++;
                        while (right_3-1>left_3&&nums[right_3-1]==nums[right_3])right_3--;
                        right_3--;
                        left_3++;
                    }
                }
                while (left_2+1<nums.length-2&&nums[left_2+1]==nums[left_2])left_2++;
            }
            while (left_1+1<nums.length-3&&nums[left_1+1]==nums[left_1])left_1++;
        }
        return res;
    }

其实也可以使用另一种去重方法:容器API,但是时间复杂度开始while比较快,毕竟使用contains需要从头比较一遍

                       ArrayList<Integer> list = new ArrayList<>();
                        list.add(nums[left_1]);
                        list.add(nums[left_2]);
                        list.add(nums[left_3]);
                        list.add(nums[right_3]);
                        if(!res.contains(list))res.add(list);
                        right_3--;
                        left_3++;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值