算法学习打卡:
一、两数之和
题目链接:
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;
}
}
方案二:
哈希解法:后面学习了代码随想录里面关于这道题的解答。用哈希表来处理这道题,更快,更灵活。学习完后,自己手敲了一遍。
解题思路:
- 首先判断数组是否为空以及数组长度是否为零,是的话,直接返回。
- 创建哈希表,key储存具体的数值,value储存数组的下标。
- 循环遍历数组,
temp=target-nums[i];
,看哈希表里面有没有temp值,如果没有,就将键值对放入哈希表,如果有对应的temp值,那就是找到了目标值,将当前的i放入res[1],map对应的temp值,放入res[0],就可以了! - 解题的时候要学会转换思路,不需要一个一个加然后判断,因为是找两数之和,寻找的值=目标值-当前的数值,再看哈希表里面有没有要寻找的值就可以了。使用键值对的方式,记录对应的下标即可。
代码示例:
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
- 快指针移动,遇到数不为0的时候,就把快指针当前指向的数放入慢指针的位置,慢指针+1
- 一直到快指针指向最后一个数,也就是没有需要再往前移动的数了。
- 从慢指针开始的位置一直到数组的末尾,全部填充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,当快指针指向的数不为零时,就进行覆盖。
解题思路:
- 如果数组没有0,快慢指针始终指向同一个位置
- 如果数组有0,快指针先走一步,慢指针就指向了0,也就是需要覆盖的位置
- 快指针指向的数不为零时,指向的数覆盖到慢指针所指向的数组下标的位置
- 快指针到结尾时,慢指针当前所指向的下标到数组的末尾均填充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 != j
、i != k
且 j != 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 。
方案一:
一开始也想到了先进行排序,然后使用双指针的方式进行求解。但是一直无法对同样的答案进行去重,因为没有想到固定一边的值,只想到了两端同时移动,中间也一直移动,导致思路非常的混乱,花费了比较长的时间,最后也没有写出对应的代码,后来稍微看了一点题目提示,需要外层循环固定一个数,再使用双指针进行夹逼就可以了。
解题思路:
- 对数组重新进行排序
- 外层循环判断nums[i]>0 如果大于零了,也就没有合适的解了,直接返回就可以了。
- 当i>0时,判断 nums[i]==nums[i-1] 进行去重操作
- left=i+1,right=nums.length-1
- while(left<right), 进行三数求和
- 当sum>0时,右指针向右移动,缩小数值。
- 当sum<0时,左指针向左移动,增大数值。
- sum=0时,将i,left,right下标所指向的值存入集合res里。左右指针同时移动。注意这里需要进行去重,跳过重复的值。同时需要判断left<right 避免出现越界的情况。
- 最后返回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。一步步提升自己,虽然剩下的时间不是很多了,继续加油!