目录
数组理论基础
概念:数组是存放在连续内存空间上的相同的数据类型的集合
704.二分查找
力扣题目给定的条件是 升序,元素不重复,元素为整数
最开始看到这个题目,我的想法 : 二分法,那肯定要从中间分哦,那就需要知道数组长度,然后除二,目标值与中间值比较大小,比完了就能确定左还是右,再然后我就不知道如何处理了.卡住了
大一下学的数据结构与算法,现在都忘得差不多了,因为觉得难,就没有好好复习过.只是应付了期末考试
我只有看看文章,来理清思路:解题方法有两种---闭区间和左闭右开区间
闭区间 |解题思路
1 while条件怎么确定
可以确定的是left < right 但是时候加上等于符号,还要看使用哪种题解方法.
闭区间的话,就要加上等于符号,因为如文章所示,假设我要查找的目标值是第一个,那就会产生left=right的情况,这样才可以找到目标值,反之第二种解题方法,不需要它们相等,就可以找到.
2 如何定区间?
left=0,必须初始化,不然走到while循环就要报错:变量可能没有初始化~
right确定为数组最后一个元素的下标
middle确定为left+((right-left)/2),而不是right/2(这是我最开始的想法),原因:当目标值大于中间值,那么闭区间中的左下标就会发生变化,left=middle+1; 再次改变middle的值,如果还是right/2,那么就会造成下标溢出的问题,所以不能这样写.
边界值和中间值的问题解决了,就可以在while循环中写if语句了,要么左,要么右,要么就是 return middle;
3 如何优化代码?
文章说的是在进入循环判断之前,就写一条语句判断目标值是否在该数组中,不在就返回-1,这样能减少无意义的循环次数,减少内存消耗.
正解:
class Solution {
public int search(int[] nums, int target) {
if(target <nums[0] || target>nums[nums.length-1]){
return -1;
}
int middle=0,left=0,right=0;//int基本数据类型 默认值是0,但是还是必须初始化,不然走到while循环就会报错
right=nums.length-1;
while(left<=right){
// middle=right/2; 为什么不能这样写,因为你想看
//目标值可能在右区间,所以左下标就会发生变化,而此时中间下标也会移动到左下标右边
//单纯地这样写,会造成下标溢出
middle=left+((right-left)/2);
if(nums[middle]>target){//左区间
right=middle-1;
}else if(nums[middle]<target){//右区间
left=middle+1;
}else{return middle;}
}
return -1;
}
}
左闭右开区
间 |解题思路
当目标值在左区间,开区间的边界值right 不会作为下标,被放在数组中作比较,所以right=middle
当目标值在右区间,处理left=middle+1;同样的开区间的边界值right 不会作为下标,被放在数组中作比较
middle的处理方法同上,优化代码也是同上.
正解:
class Solution {
public int search(int[] nums, int target) {
if(target<nums[0] || target >nums[nums.length-1])
{return -1;}
int left=0,right,middle;
right=nums.length;
while(left<right){
middle=left+((right-left)/2);
if(nums[middle]>target){
right=middle;
}
else if(nums[middle]<target){
left=middle+1;
}
else{
return middle;
}
}
return -1; }
}
27.移除元素
最开始的思路就是暴力解法,写嵌套for循环,而且还试想过用增强for循环,但是发现不对劲,就没写.但是写嵌套循环,一开始我还是没有明确的思路,还是参考了文章才写的.
---暴力解法---
第一版:测试失败
class Solution { public int removeElement(int[] nums, int val) { for(int i=0; i<nums.length;i++){ if(nums[i]==val){ for(int j=i; j<nums.length;j++){ nums[j]=nums[++j];//这里 } } } return nums.length; } }
原因:我没考虑到下标越界,考虑不全.报错了才发现,这么写,在内层第二轮循环就会出问题;然后我就去参考了文章,接下来出现了第二个问题
第二版:测试失败
class Solution { public int removeElement(int[] nums, int val) { for(int i=0; i<nums.length;i++){ if(nums[i]==val){ for(int j=i+1; j<nums.length;j++){ nums[j-1]=nums[j]; } } } return nums.length; } }
原因:没考虑到nums[i+1]==val这种情况,我这样写代码就会造成本来还有需要移除的元素,但是i跑了,不管了.所以当数组移除了一个目标值,应该保证下标还在原来的位置,这样等下一轮if判断,就可以知道是否存在nums[i+1]==val这种情况了.
第三版:测试失败
class Solution { public int removeElement(int[] nums, int val) { int nLength=nums.length; for(int i=0; i<nLength;i++){ if(nums[i]==val){ for(int j=i+1; j<nLength;j++){ nums[j-1]=nums[j]; } } i--; nLength--; } return nLength; } }
原因:这个时候虽然写了第二版没考虑的代码,但是位置放错了,我当时不懂,没反应过了,我只是觉得写在if外面一样的执行啊,凭什么不可以啊,等我写好备注后,才明白,肯定不可以啊,这两句代码是要满足有元素被移除才执行,反之不动,我要是一直让下标前移,那就是毫无意义的,而且无法通过测试.
正解:
class Solution {
public int removeElement(int[] nums, int val) {
int nLength=nums.length;
for(int i=0; i<nLength;i++){
if(nums[i]==val){
for(int j=i+1; j<nLength;j++){
nums[j-1]=nums[j];
}
i--;
nLength--;
}
}
return nLength;
}
}
---快慢指针---
起初我不能理解为什么是当快指针不等于目标值才移动元素,反而等于时,啥也不做,只需要进行下一轮循环.我自己去写了一下,我想的那种,发现有点行不通,看着这么晚了,就参考文章写代码,还是有点不理解,就在纸上模拟了一遍过程,突然就懂了,这个快慢指针法,厉害的勒~这逻辑妙蛙种子!!!
就是:快指针负责遍历数组,找到排除了目标值的元素,慢指针负责更新 新数组的下标位置,
整个过程就是,找到要排除的元素,慢指针就不动,反之就更新下标,知道循环遍历完成,慢指针在哪里,那里就是数组的长度.
正解
class Solution {
public int removeElement(int[] nums, int val) {
int slowIndex=0;
for(int fastIndex=0;fastIndex<nums.length;fastIndex++){
if(nums[fastIndex]!=val){
nums[slowIndex]=nums[fastIndex];
slowIndex++;
}
}
return slowIndex;
}
}