今日任务
1.二分查找
2.移除
1.二分查找
此题虽然从思路上较为简单,但是对于边界条件,很容易混淆,本质上是对元素的交并不够清晰,例如快速抛出几个问题
1.在使用while循环中,有时是left <= right,有时却是left < right,这其中肯定是有区别的。
2.在更新左/右边界上,方式不一样,有时是right = middle - 1,有时却是 right = middle。
首先此类问题可以大致分为左闭右闭区间和左闭右开区间。
1.左闭右闭区间和左闭右开区间的区别
例如在数组1,2,3,4,7,9,10中,如果使用左闭右闭区间,则用图直观表示为
其中left边界设置为0,right边界设置为6,因为在数组arr[left]或者arr[right]上都能取到元素,故此为左闭右闭区间
如果使用左闭右开区间,则用图直观表示为
同上分析,右边界无法取到元素,故此为左闭右开区间
注意这两种方式,同为左闭,这为后来理解可以提供些许方便之处。
开始对其进行具体分析,首先我们应该能熟练二分法思想,本质上就是不断更新左右边界而能更快查找到元素,所以对其思路的分析便不再续写,我们的重点是边界条件的分析。
1.对于while中条件的分析
在左闭右闭区间中,当我们使用while循环时,应该使用left < right还是left <= right呢?我们首先应该关注while的用法,只要不满足while语句的条件时,便会自动跳出循环,由此,当left < right时,即当left = right时或者left > right时,就会跳出循环了。而left = right值得我们再去思索思索。
首先我们应该考虑,存不存在left = right时,我们仍能在区间内取到一个元素,不然啥元素都取不到,连比较都没法进行比较了。
我们可以举个例子
当left = 1,right = 1,在区间上可以表示为[1,1],根据数学知识,我们能成功取得元素1。所以left = right应该完美嵌合左闭右闭区间的合法区间。所以要是在while循环中直接写left < right,当left = right时,明明这是满足题意的合法条件,但是在该循环的条件中被判定为不合法,就会提前跳出循环。
而在左闭右开区间呢?
我们同样进行考虑while循环中的条件写法。
牢牢记住,我无论left和right怎么变,我一定要能在left和right的区间中取到元素。
再举个例子
还是当left = 1,right = 1,在左闭右开区间就表示为[1,1),这能取到元素吗,显然不行,故left = right这种写法肯定不能满足。
2.对于middle的更新
首先对左闭右闭区间进行分析,我们先给出代码
if(nums[middle] > target) {
right = middle-1;
}
else if(nums[middle] < target) {
left = middle+1;
}
先对右边界的更新进行分析。注意看if的条件,里面的nums[middle]是不是已经被拿来和target目标值比较了,已经比它大了,我后续更新我的right时还有必要取到它吗?注意,是左闭右闭区间,所以右边是可以取到元素的,所以没必要啊,我都已经证明了。
左边界我们可以不急,因为左闭右闭区间和左闭右开区间都有个左闭,这也是共同点,我们可以最后一次性全部分析。
再对左闭右开区间进行分析,我们给出代码
if(nums[middle] < target){
left = middle + 1;
}
else if(nums[middle] > target){
right = middle ;
}
我们可以看到right直接赋值为middle了。注意这是左闭右开区间,我们nums[middle]是不是已经分析过了,它大于target目标值没问题,他的索引也为middle也没问题,关键是我们是左闭右开区间,我哪怕直接使right = middle,我右边反正也取不到元素我也不怕你重复了,故条件可直接写为right = middle。
最后,对左边条件的分析,我感觉可以忽略了,就和左闭右闭区间的右闭分析思路是一样的。
2.移除数组中元素
这题仍然是一道较为简单的题目,因为题意足够浅显,通过暴力可求解,但这题精髓是双指针法,接下来对双指针法进行分析。暴力法代码会贴在后面,可供参考。
目前我的理解是双指针法,不是真的是要使用两个指针来进行求解,在数组中,我们都是可以使用index (索引)来代替指针的作用的。
我们先对总体思路提一下,首先,我们可以知道,数组中的元素删除本质不是删除,只是对其进行覆盖,就像老师叫你擦黑板,你为了胡弄这份差事,你可以直接拿一张大黑纸覆盖。所以我们就能知道,数组在进行删除中,并未涉及到真正意义上的擦除,而我们拥有索引这个利器。
引入Slow指针和Fast指针, 其实,我们也可以写作read指针和write指针,read指针只负责对这个数组进行遍历和条件判断,真正的更改这个数组元素是依靠write指针来写入的。
首先,我们可以使用循环,用read指针对这个数组进行遍历,因为这个数组本来就是有元素的,我们进行遍历的时候,也可以顺便进行条件判断,
当read指针指向的元素不等于我们需要删除的元素时,也就意味啥事不干,直接进行赋值语句arr[write] = arr[read]。
而当read指针指向的元素等于我们需要删除的元素时,我就选择视而不见,直接跳过赋值语句。
我们可以使用图像来辅助理解
最后我们贴出暴力求解的方法
for (int i = 0; i < size; i++) {
if (nums[i] == val) {
for (int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--;
size--;
}
}