Bootstrap

力扣算法刷题记录:

算法学习打卡:

一、两数之和

题目链接:

https://leetcode.cn/problems/two-sum/description/?envType=study-plan-v2&envId=top-100-liked

题目描述:

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

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

提示:

  • 2 <= nums.length <= 104
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

**进阶:**你可以想出一个时间复杂度小于 O(n2) 的算法吗?

方案一:(暴力解)

暴力解法:使用两个循坏进行解答(好久没有刷题了,暂时只能想到这样了),这个很容易就想出来,就不赘诉思路了。

代码示例:
public class Solution1 {
    public int[] twoSum(int[] nums, int target) {
        int[] a=new int[2];
        for(int i = 0; i <nums.length;i++){
            for(int j = i+1;j<nums.length;j++){
                int sum=nums[i]+nums[j];
                if(sum==target){
                    a[0]=i;
                    a[1]=j;
                    break;
                }
            }
        }
        return a;
    }
}

方案二:

哈希解法:后面学习了代码随想录里面关于这道题的解答。用哈希表来处理这道题,更快,更灵活。学习完后,自己手敲了一遍。

解题思路:
  1. 首先判断数组是否为空以及数组长度是否为零,是的话,直接返回。
  2. 创建哈希表,key储存具体的数值,value储存数组的下标。
  3. 循环遍历数组,temp=target-nums[i];,看哈希表里面有没有temp值,如果没有,就将键值对放入哈希表,如果有对应的temp值,那就是找到了目标值,将当前的i放入res[1],map对应的temp值,放入res[0],就可以了!
  4. 解题的时候要学会转换思路,不需要一个一个加然后判断,因为是找两数之和,寻找的值=目标值-当前的数值,再看哈希表里面有没有要寻找的值就可以了。使用键值对的方式,记录对应的下标即可。
代码示例:
public int[] twoSum(int[] nums, int target){
    int[] res=new int[2];
    if(nums == null||nums.length==0){
        return res;
    }
    HashMap<Integer,Integer> map = new HashMap<>();
    for(int i=0;i<nums.length;i++){
        int temp=target-nums[i];
        if(map.containsKey(temp)){
            res[1]=i;
            res[0] = map.get(temp);
            break;
        }
        map.put(nums[i],i);
    }
    return res;
}

二、移动零

题目链接:

https://leetcode.cn/problems/move-zeroes/description/?envType=study-plan-v2&envId=top-100-liked

题目描述:

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

示例 2:

输入: nums = [0]
输出: [0]

提示:

  • 1 <= nums.length <= 104
  • -231 <= nums[i] <= 231 - 1

方案一(代码量多):

使用快慢指针解法即可。因为知道后面补充的元素都是0,所以可以直接进行覆盖,不需要一个一个往前移动,一开始没有考虑到这一点,所以思考了很久,遇到了一个零就把所有的往前移动,遇到一个就把所有的往前移动,非常的麻烦,也没有写出相应的代码,后来想到了快慢指针以及后面需要填充的是0,直接进行覆盖就可以了。虽然是个easy题,但想到了好开心!

解题思路:
  1. 慢指针从头开始移动,一直到遇到第一个零停止,此时就是需要交换的位置。快指针=慢指针+1
  2. 快指针移动,遇到数不为0的时候,就把快指针当前指向的数放入慢指针的位置,慢指针+1
  3. 一直到快指针指向最后一个数,也就是没有需要再往前移动的数了。
  4. 从慢指针开始的位置一直到数组的末尾,全部填充0即可。
代码示例:
public void moveZeroes(int[] nums) {
    if(nums!=null && nums.length!=0 &&nums.length!=1 ){
        int fast=0;
        int slow=0;
        int len = nums.length;
        for (;slow<len;slow++){
            if(nums[slow]==0){
                break;
            }
        }
        fast=slow+1;
        while(fast < len){
            if(nums[fast]!=0){
                nums[slow]=nums[fast];
                slow++;
            }
            fast++;
        }
        for (;slow<len;slow++){
            nums[slow]=0;
        }
    }
}

方案二(代码量少)

后面去看了力扣大佬的详解,其实思路是差不多的,但是自己的归纳能力还是差了点,多了一个寻找0的代码。其实在遇到0之前可以自己覆盖自己,总的来说就是快指针遇到0了,就先走一步,此时慢指针对应的位置就是0,当快指针指向的数不为零时,就进行覆盖。

解题思路:
  1. 如果数组没有0,快慢指针始终指向同一个位置
  2. 如果数组有0,快指针先走一步,慢指针就指向了0,也就是需要覆盖的位置
  3. 快指针指向的数不为零时,指向的数覆盖到慢指针所指向的数组下标的位置
  4. 快指针到结尾时,慢指针当前所指向的下标到数组的末尾均填充0
代码示例:
public void moveZeroes2(int[] nums){
    int slow=0;
    for(int fast=0;fast<nums.length;fast++){
        if(nums[fast]!=0){
            nums[slow]=nums[fast];
            slow++;
        }
    }
    for (;slow<nums.length;slow++){
        nums[slow]=0;
    }
}

三、三数之和

题目链接:

https://leetcode.cn/problems/3sum/description/?envType=study-plan-v2&envId=top-100-liked

题目描述:

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

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

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。

方案一:

一开始也想到了先进行排序,然后使用双指针的方式进行求解。但是一直无法对同样的答案进行去重,因为没有想到固定一边的值,只想到了两端同时移动,中间也一直移动,导致思路非常的混乱,花费了比较长的时间,最后也没有写出对应的代码,后来稍微看了一点题目提示,需要外层循环固定一个数,再使用双指针进行夹逼就可以了。

解题思路:
  1. 对数组重新进行排序
  2. 外层循环判断nums[i]>0 如果大于零了,也就没有合适的解了,直接返回就可以了。
  3. 当i>0时,判断 nums[i]==nums[i-1] 进行去重操作
  4. left=i+1,right=nums.length-1
  5. while(left<right), 进行三数求和
  6. 当sum>0时,右指针向右移动,缩小数值。
  7. 当sum<0时,左指针向左移动,增大数值。
  8. sum=0时,将i,left,right下标所指向的值存入集合res里。左右指针同时移动。注意这里需要进行去重,跳过重复的值。同时需要判断left<right 避免出现越界的情况。
  9. 最后返回res就好了
代码示例:
public List<List<Integer>> threeSum(int[] nums) {
    List<List<Integer>> res=new ArrayList<>();
    Arrays.sort(nums);
    for (int i=0;i<nums.length-2 ;i++){
        if (i>0 && nums[i]==nums[i-1]){
            continue;
        }
        if(nums[i]>0){
            break;
        }
        int left=i+1;
        int right=nums.length-1;
        while(left<right){
            int sum=nums[i]+nums[left]+nums[right];
            if(sum==0){
                res.add(Arrays.asList(nums[i],nums[left],nums[right]));
                while (left<right && nums[left]==nums[left+1]){
                    left++;
                }
                while (left<right && nums[right]==nums[right-1]){
                    right--;
                }
                left++;
                right--;
            }else if(sum>0){
                right--;
            }else {
                left++;
            }
        }
    }
    return res;
}

总结:

今天一共写了三道题,两道easy,一道中等,加上写总结的时间花了差不多四个小时。一直在学习java的各种技术,学习黑马的javaweb的项目,一年多没有写算法了,所以思考的能力大大的下降了,然后表达的能力也有所下降,写思路的时候,有些不知道如何表达,写的也较为潦草。两数求和一年前能够写出来,现在还需要花费较多的时间,希望自己一点点慢慢捡起来学习吧。

但总的来说,今天还是有收获的,起码还记得双指针。以及可以对给定的数组进行处理,处理完后再进行对应的求解。但思维还是不够发散,刷的题目还是不够。希望后续能够每天刷两三道力扣hot100。一步步提升自己,虽然剩下的时间不是很多了,继续加油!

;