704、二分查找
重点是如何选择区间的定义,这关系到我们的更新逻辑及判断条件。
1、定义左闭右闭区间
因为是左右都可以取到取值,那么是有意义的,那么循环的判断条件则为
其次,当 时,要或者是,因为当前这个的取值已经不是,并且我们的区间是左右都闭合,即左右都可以取到的,那么这个再取值就没意义了,那么就可以舍弃掉当前这个
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size()-1;
while(left <= right){
int middle = (left + right) / 2;
if (nums[middle] < target){
left = middle + 1;
}else if (nums[middle] > target){
right = middle - 1;
}else{
return middle;
}
}
return -1;
}
};
2、定义为左闭右开区间
定义区间为:,那么这个我们是一直取不到取值的,因此循环的判断条件为,等于已无意义;其次在更新时也要按照闭合和开的区间边界来区分开,因此右边界一直不会取到,那么不能取否则就会忽略掉的判断。 因此更新公式为 以及
其次,初始化的时候由于右边界取不到,因此的话会将结尾元素忽略掉,因此取
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size(); #注意这里不再是size()-1,因此这个边界我们取不到
while(left < right){
int middle = (left + right) / 2;
if (nums[middle] < target){
left = middle + 1;
}else if (nums[middle] > target){
right = middle;
}else{
return middle;
}
}
return -1;
}
};
能不能定义为左开右闭?
不行!因为这样定义的话 在初始化的时候就不能等于0,因为这样会由于区间左边是开的,取不到左边界的值,那么就忽略掉的值。而再往左边走就是了,整个后面的逻辑就乱了,因此不可以。
35、搜索插入位置
本题跟二分查找类似,增加一个找不到如何返回插入的索引即可
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
int middle;
while (left <= right){
middle = ( left + right ) / 2;
if (nums[middle] < target){
left = middle+1;
}else if(nums[middle] > target){
right = middle-1;
}else{
return middle;
}
}
if (nums[middle] > target){
return middle;
}else{
return middle + 1;
}
}
};
在我的代码中找不到后我进行了大小的判断,从而解决了插入位置索引的问题。而代码随想录中的解决方案更是灵活
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
int middle;
while (left <= right){
middle = ( left + right ) / 2;
if (nums[middle] < target){
left = middle+1;
}else if(nums[middle] > target){
right = middle-1;
}else{
return middle;
}
}
return right + 1
}
};
因为插入到其中只可能有四种情况:
- 插入到所有元素的左边:即比所有元素都小,那么在判断的时候就是不断向靠近,直接的时候仍然是 因此就 ,那么此时,而要返回的位置为第一个位置即索引0,因此返回
- 插入到所有元素的右边:即比所有元素都大,那么在判断的时候就是不断向靠近,直接的时候仍然是 因此就 ,那么此时,而要返回的位置为最后一个元素后面,即索引位置为,因此返回
- 插入到中间元素的左边:即比当前对应的元素小,此时已经是,因此更新为,而正确的插入位置就是,因此返回
- 插入到中间元素的右边:即比当前对应的元素大,此时已经是,因此更新为,此时位置为,而正确的插入位置就是,因此回
34、在排序数据中查找元素的第一个和最后一个位置
该题目一开始的思路是想着初始化数组然后左右边界同时判断,但觉得能力有限逻辑不清楚,因此看了代码随想录的提醒,即左右边界分开求解便思路清晰,具体代码如下:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int rightBorder = getRightBorder(nums,target);
int leftBorder = getRLeftBorder(nums,target);
if (rightBorder == -1 || leftBorder == -1){
return {-1,-1};
}else{
return {leftBorder,rightBorder};
}
}
int getRightBorder(vector<int>& nums,int target){
int rightBorder = -1;
int left = 0;
int right = nums.size()-1;
while(left <= right){
int middle = (left + right) / 2;
if( nums[middle] < target ){
left = middle+1;
}else if( nums[middle] > target ){
right = middle-1;
}else{
rightBorder = middle;
left = middle+1;
}
}
return rightBorder;
}
int getRLeftBorder(vector<int>& nums,int target){
int leftBorder = nums.size();
int left = 0;
int right = nums.size()-1;
while(left <= right){
int middle = (left + right) / 2;
if( nums[middle] < target ){
left = middle+1;
}else if( nums[middle] > target ){
right = middle-1;
}else{
leftBorder = middle;
right = middle-1;
}
}
return leftBorder;
}
};
主要思路是初始化边界都为,只要其中存在与相同的元素那么就一定能够通过二分法查找到,就一定会修改边界,因此只要返回的两个边界不为就直接输出两个边界即可。返回的若其中一个是那么就要输出都是
其次就是判断边界中我的边界更新条件与代码随想录中的不同,但都是可以通过的。
69、x的平方根
同样是用二分查找,主要是longlong数据类型的使用
class Solution {
public:
int mySqrt(int x) {
int left = 0;
int right = x;
int ans = -1;
while(left <= right){
int middle = (left + right) / 2;
if ((long long) middle * middle <= x){
ans = middle;
left = middle + 1;
}else{
right = middle - 1;
}
}
return ans;
}
};
以下代码是不行的:
int middle = (left + right) / 2;
long long middlePow = middle * middle;
这样会显示溢出,在的时候,因为是类型,在相乘之后也想要返回一个类型,但是太大的数字在这里就发生了溢出,因此就算是一个类型来接收也不行。可以这样做:
int middle = (left + right) / 2;
long long middlePow = (long long)middle * middle;
在返回之前就用一个来将返回值的类型修改,防止溢出。
367、有效的完全平方数
这部分耶可以用二分查找完成,具体逻辑只是在找到的时候返回值不同而已,找到我们立刻返回一个,如果正常退出循环即说明没有找到,因此返回。
class Solution {
public:
bool isPerfectSquare(int num) {
int left = 0;
int right = num;
while(left <= right){
int middle = (left + right) / 2;
long long middlePow = (long long)middle * middle;
if(middlePow < num){
left = middle + 1;
}else if(middlePow > num){
right = middle - 1;
}else{
return true;
}
}
return false;
}
};