一、二分法
首先,二分法搜索的前提是数组必须是有序的。然后在一个有序的数组里面找到目标值。
while(left<=right){
target<nums[mid] 更新右边界 right=mid-1
target>nums[mid] 更新左边界 left=mid+1
如果相等,说明找到了,return mid;
}
注意的点:while循环中的条件是影响到下面更新边界操作的。借助一下开闭区间来理解(卡尔那边学到的)
1.如果是left<=right,说明左右结点是可以相等的,这个区间是[left right]的,那么在更新边界的时候,因为边界值都是可以取到的(闭区间),所以left=mid+1或者right=mid-1,都是允许的。
2.如果left<right,说明左右结点不可以相等,也就是说区间是[left right)的。那么在更新边界的时候,左边界是闭的可以取到值,而右边界是开的不可以取到。
因此在更新右边界right的时候要变成mid,这样[left,mid),才可以取到mid-1。
更新左边界left的时候,因为是闭的,所以mid+1,仍然可以。
为了方便,推荐while用<=条件来写,这样就不用考虑能不能取到边界值了。
二、移除元素(双指针):
这里的双指针都是从同一个起点开始的。
使用快慢指针,其实是对数组起到了一个筛选的作用。筛选的条件就是值是否等于val,如果等于就被淘汰。我觉得快指针主要起筛选的作用,慢指针起一个保存位置的作用。
for(int fast=0;fast<nums.length;fast++){
//开始筛选
if(nums[fast]!=val){
nums[slow++]=nums[fast];成功筛选
}
}
三、有序数组的平方(双指针):
这里的双指针是从两边开始的。(是基于题目所给的数组是非递减排序的,并且存在负数)
从这里我们可以推断出:两边的值的平方一定是最大的,并且从两边到中间是从大到小的。
思路:类似于合并排序,比较两边平方大小,大的放进去并且移动。
if(nums[slow]*nums[slow]>nums[fast]*num[fast])
result[size--]=nums[slow++]*nums[slow]
四、长度的最小子数组:(暴力法/滑动窗口)
暴力法:
对该题的一些自我理解:因为要求的的最小子数组,因此要求是连续的。可以使用暴力解法:依次从i开始,只要sum>s,就算出此时子数组的长度,并且更新。
public int minSubArrayLen(int target, int[] nums) {
int subLength;
Integer result=Integer.MAX_VALUE;
for(int i=0;i<nums.length;i++){
int sum=0;
for(int j=i;j<nums.length;j++){
sum+=nums[j];
if(sum>=target){
subLength=j-i+1;//当sum大于target的时候 那么我的长度为j-i+1
result=subLength<result?subLength:result;
break;//因为要子数组 所以以i为开头的子数组一旦满足 就break
}
}
}
if(result==Integer.MAX_VALUE)
return 0;
else return result;
}
但是使用暴力法时间复杂度为O(n*n)会导致时间超出限制。因此使用O(n)的滑动窗口法
滑动窗口(双指针):个人理解:依旧是一个i慢指针、j快指针。在i和j移动的过程中按照sum>target形成滑动窗口。j是用来确定滑动窗口的右边界,确保sum>target。i是用来不断寻找最小长度。
代码:
int i=0;
for(int j=0;j<nums.length;j++){
sum+=nums[j];
while(sum>=target){
int subL=j-i+1;
result=subL<result?subL:result;
//从这里开始 慢指针开始优化subL
sum-=nums[i];i++;
}
}
五、螺旋矩阵:重点:旋转的次数(也是循环的次数)是(n/2)
首先要确定一个规则,当每一行的最后一个不由自己处理,交给下一个处理,这样每一次都处理相同的个数,for循环就统一了。当填写完一圈之后,准备填写第二圈之前,要将起始点的坐标和每一次终止的位置进行更新。(犯的错误,从左到右和从上到下的时候,循环的终止条件是<n-count 从右到左和从下到上的时候,循环的终止条件应该是>startX/startY)
public int[][] generateMatrix(int n) {
int [][]martix=new int[n][n];
int startX=0;
int startY=0;
int offSet=1;//每次循环要减的值
int cnt=1;
//开始给矩阵填值
while((n/2)!=0){
//从左到右
int i=startX;//行索引
int j=startY;//列索引
for(;j<n-offSet;j++){
martix[i][j]=cnt++;
}
//从上到下
for(;i<n-offSet;i++){
martix[i][j]=cnt++;
}
//从右到左
for(;j>startY;j--){
martix[i][j]=cnt++;
}
//从下到上
for(;i>startX;i--){
martix[i][j]=cnt++;
}
startX++;
startY++;
offSet++;
n/=2;
}
if(n%2==1){
martix[startX][startY]=cnt;
}
return martix;
}
六:移除链表元素(较简单)
我认为,链表和数组的结构是类似的,只不过前者的空间地址不同,需要通过next链接。并且增删元素比较容易,但是查找的话后者更容易。难点:最后返回的时候一定是虚拟头结点的next,不能是head,因为head也在需要移除的范围里面.比如这个例子
七:设计链表()
遇到的问题:
1.在获取第下标为index值的时候,不知道该如何遍历(怕遍历到下一个或者是遍历到上一个,这种问题经常困扰我),这里我选择从head开始,也就是从第一个结点,下标为0,我要获取下标为index的结点,那么就需要遍历到index,因此使用一个for循环就可以。
for(int i=0;i<=index;i++)
cur=cur.next;
2.在添加结点或者删除结点的时候,一定要更新size
八、反转链表(双指针/递归)
刚开始我在思考的时候:想到了slow fast指针同时后移动,但是连接的时候没有想到用temp保存fast.next;
双指针
slow fast temp
while(fast!=null){
temp=fast.next;//存储一下
fast.next=slow;
slow=fast;
fast=temp;
}
return slow;
递归法:
道理和双指针类似
public ListNode reverse(ListNode slow,ListNode fast){
if(fast==null)return slow;
ListNode temp=new ListNode();
temp=fast.next;
fast.next=slow;
reverse(fast,temp);
}