704.二分查找
题目
给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
代码
class Solution {
public int search(int[] nums, int target) {
int left, right, mid;
left = 0;
right = nums.length - 1;
while(left <= right){
mid = left + (right -left) / 2;
if(nums[mid] > target){
right = mid - 1;
}
else if(nums[mid] < target){
left = mid + 1;
}
else {
return mid;
}
}
return -1;
}
}
总结
1.原理:左指针left和右指针right包住数组的头尾,循环判断目标target和mid值的大小,如果mid大,说明target在区间右边,令right右移,如果mid小,说明targer在区间左边,令left左移,直到循环终止。
2.核心算法(左闭右闭):while(left <= right)是循环条件 ,right =mid-1,left = mid +1。为什么循环条件的有等号,因为当left==right时,当前的元素其实还没有和target做过比较,因此也要进入while循环。
3.JAVA语法错误:nums.length 注意语法,后边没有()
4.代码错误:right = nums.length-1,因为是右闭区间这个-1不要漏掉。最后不要忘了如果找不到要返回-1。
刷题第二次总结
1.判断nums[mid]和target的大小时,想清楚哪个才是我们要的目标区间,不要写反了。
2.while(left <= right)是循环条件,这个等号还是要举个例子理解,不能死记硬背。
27.移除元素
题目
给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用 O(1)
额外空间并 原地 修改输入数组。
代码
class Solution {
public int removeElement(int[] nums, int val) {
int i, j;
i = 0;
for(j = 0;j < nums.length;j++){
if(nums[j] != val){
nums[i++] = nums[j];
}
}
return i;
}
}
总结
(1)原理:两个指针,快指针进行遍历,用于判断当前元素是否等于val,慢指针用于指向当前更新元素位置的下标在哪里,所以如果当前元素是val,就让快指针继续往后走,如果当前元素不是val,就要把当前元素放到慢指针的下标位置,同时其++,最后返回i,因为最后一次++,正好等于长度。
刷题第二次总结
1.一定理解并想清楚快慢指针的作用,slow指向的是要插入新元素的下标位置,fast指向的是我们需要的元素位置,这样后边的return slow才能理解到位。
2.函数要返回修改后的数组长度,这里不能用length函数,因为数组只是数值被修改了,长度并没有变化,这里要返回slow,因为slow是要插入新元素的下标位置,正好等于新的length。
997.有序数组的平方
题目
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
代码
class Solution {
public int[] sortedSquares(int[] nums) {
int i, j, k;
int[] result = new int[nums.length];
k = nums.length - 1;
for(i = 0,j = nums.length-1;i <= j;){
if(nums[i] * nums[i] > nums[j] * nums[j]){
result[k--] = nums[i] *nums[i];
i++;
}
else{
result[k--] = nums[j] *nums[j];
j--;
}
}
return result;
}
}
总结
(1)原理:左指针从头开始,右指针从尾开始,向中间靠近,判断哪个的平方更大,就往结果数组里面存,如果左更大,左指针++,如果右更大,右指针--,循环终止条件是<=,有等于是因为,左右指针相同的时候,还有最后一个元素没有存到结果数组里。
(2)语法问题:int[] result = new int[nums.length];用于开辟一个长度的数据,用来存储平方后的数组,这次这里没写出来。
(3)注意点:最后要return 结果数组,不要忘记了
刷题第二次总结
1.循环终止的条件要连接好,i和j指向的是没有存过平方数的下标,i<j时还有两个数没存呢,所以i=j时,还要存最后一个数的平方
2.这里的空间复杂度至少是O(n),必须要开辟一个result数组,想把平方后的数直接存在原数组是不行的,因为平方数数组是从后往前存的,而原数组右边的数可能还没被访问到就被覆盖了。
209.长度最小的子数组
题目
给定一个含有 n
个正整数的数组和一个正整数 target
。找出该数组中满足其总和大于等于 target
的长度最小的 连续子数组[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
代码
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int i, j, sum, sublength, result;
i = j = sum = sublength = 0;
result = Integer.MAX_VALUE;
for(j = 0 ; j < nums.length ;j++){
sum += nums[j];
while(sum >= target){
sublength = j - i + 1;
result = result < sublength ? result : sublength;
sum -= nums[i];
i++;
}
}
if(result == Integer.MAX_VALUE){
return 0;
}
return result;
}
}
总结
(1)原理:找满足和大于等于目标s的最小连续区间长度,用j遍历数组,表示窗口的左区间,用i表示窗口的右区间,如果窗口的元素和不足s,j就要一直++,让窗口变大,一旦窗口和满足s,就计算当前窗口大小,保存最小的窗口长度。注意,while(sum>=target),这里是while不是if,只要当前的滑动窗口值满足s,i就要一直右移找到直到不满足s,然后再调整j。
(2)语法错误:result = Integer.MAX_VALUE不会写,表示最大的int整数。
(3)算法错误:sum -= nums[i]; i++;这里出错了,因为要把滑动窗口元素和减去开始的值,i才能移动,这里两个语句的顺序不能换,一定要先把sum里面第i个元素减去,才能把滑动窗口i++。j < nums.length,这个length不用减1,因为是小于。
(4)注意,返回长度的时候多一次判断,如果result不变就是初始状态,就说明不存在满足s的区间,要return0,这个不要漏掉了。
刷题第二次总结
1.这一次的代码写的不太一样,就再放一次吧。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int i = 0;
int j = 0;
int sum = 0;
int result = nums.length + 1;
while(j < nums.length){
sum += nums[j];
while(sum >= target){
result = result < j - i + 1 ? result : j - i + 1;
sum -= nums[i];
i++;
}
j++;
}
return result == nums.length + 1 ? 0 : result;
}
}
2.result的初始值必须是一个较大数,由于Integer.MAX_VALUE容易记不住,result也可以用num.length + 1。原因如下,因为如果能找到满足序列,长度肯定是一个小于等于num.length的整数,直接返回。如果没找到,sum永远不可能大于target,result压根没有被修改还是等于num.length + 1,这时候return 0就行。
3.再说一下代码的逻辑,核心是对滑动窗口i和j的作用。
第一个循环可以用while,也可以用for,区别就是j++的位置而言。第一个循环用于j对nums进行遍历,不断寻找满足sum>=target的区间。
第二个while循环,这个就要好好理解一下了。这里千万不能写成if,因为我们要的是满足条件的最短区间。举个例子,前一个满足target=7的区间是2134,这里result是4,然后我们让i++,区间变成134,这里sum仍然满足7,如果用if,这一段区间就会错算出3,然后j就会继续往后走了。而我们要的是最短区间34。当134满足7的时候,i还有继续++缩小区间为34才行。
核心就是要好好理解i和j到底是如何移动确定区间的,即j的作用就是一直向后搜索,帮我们扩大区间,直到满足7。这时,需要判断当前从i-j的区间中,i最多移动到哪。而i需要缩小到正好满足7的最小区间,如果i不用while移动,i可能找到的就不是最小区间了。
59.螺旋矩阵II
题目
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
代码
class Solution {
public int[][] generateMatrix(int n) {
int startx = 0;
int starty = 0;
int count = 1;
int offset = 1;
int loop = n / 2;
int[][] martrix = new int[n][n];
while(loop-- > 0){
int i = startx;
int j = starty;
for(;j < n - offset;j++){
martrix[i][j] = count++;
}
for(;i < n - offset;i++){
martrix[i][j] = count++;
}
for(;j > starty;j--){
martrix[i][j] = count++;
}
for(;i > startx;i--){
martrix[i][j] = count++;
}
startx++;
starty++;
offset++;
}
if(n % 2 == 1){
martrix[n/2][n/2] = count;
}
return martrix;
}
}
总结
(1)原理:n*n矩阵,要走n/2圈,用startx和starty表示每一圈的起始位置,每走完一圈start要++,offset是多余的边界,每一圈结束也要++,再根据左闭右开,进行四个for循环,注意for(i),如果i不用赋值,就不要写i了,会报错。直接不写就行。
(2)语法错误:二维数组是这样定义的int[][] martix = new int[n][n];
(3)语法错误:while(loop--)不能这样写会编译出错,说int和boolen有问题,要写成while(loop-- > 0)才行
(4)注意:如果n是奇数,那么最后要把n/2的元素继续放count++;count++表示的是count增1,但是整个式子大小不变,所以初始count=1
刷题第二次总结
1.核心就是loop,offset,startx,starty参数,以及四个for循环的区间想明白就行。
2.几个参数的功能和初始化如下:
loop表示参数,loop=n/2,循环条件是loop>0
offset表示每一圈的边界宽度,左闭右开,初始边界长度=1,然后每一圈结束,要++。
startx和starty表示每一圈的起始位置,初始值=0,然后每一圈结束,要++。
3.四个for循环的区间如下设置:
第一个:j从starty到n-offset,for(;j < n - offset;j++)
第二个:i从statyx到n-offset, for(;i < n - offset;i++)
第三个:j从第一个for的结束位置到starty,for(;j > starty;j--)
第四个:i从第二个for的结束位置到startx,for(;i > startx;i--)
PS:个人的学习方法
先看代码随想录的文字学习理解,再看对应的b站视频加深理解。把3-5题的内容完成后,在leetcode上一起刷,如果运行出错了,在对应提交的备注里面写上自己不会的点。第二天复习的时候,不看任何资料前,打开leetcode直接手撕,如果一次性通过就没事,如果还有什么错误,继续在提交的备注里写不会的点。最后,把这两次的出错点+解题原理在CDSN进行。
刷题第二次,一般就是直接看题目,在纸上理一下思路,一般都有思路的,然后就直接去力扣上写。没有思路的话,就可能当时第一遍根本没学好,可能需要再看一下代码随想录网站的笔记了。力扣上把代码写了,若没有运行不出错就来博客这边看一下笔记+理一下核心的算法思路。如果运行还出错,可能是算法的细节也可能是语法问题,就到博客整理出错误点+理一下核心的算法思路。这一轮基本上要更加理解题目的解题思路和算法逻辑了。第一轮的时候,可能写的代码和代码随想录基本一样,因为是学别人的思路写的,这一轮可能写的就是自己理解的成果了,如果两次代码出入比较大的话,这边我也会更新第二版的代码。