leecode35.搜索插入位置
这道题有一个要求,就是要求算法复杂度必须是logn。那么大家第一时间想到的应该就是用二分法做题
但是这个题目还有个要求,就是要如果没有查询到位置,就要返回元素应该插入的位置。我们来模拟以下加入没有找到相应元素。因为题目有说列表是有序的。
[1, 3, 5, 7]。假设我们的target是4,那么循环的left,right,middle应该为
第一次:left = 0, right = 3, middle = 1; 3 < target;left = middle+1=1
第二次:left=1,right=3,middle=2,5>target;right=middle-1=2
第三次:left=1,right=2,因为程序是下取整原则,每次二分到最后,肯定left和right都会落到两个相邻的值上,又因为数组是排序的。假设要找的taget不存在,left+right(left+1)的一半会等于left,这时nums[middle]小于target,left=middle+1。
这时左右届都是right,middle也会等于right,nums[right] > target, right= middle-1。这时循环会终止,因为right < left。这时我们要插入的值永远在right后面
上代码
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0;
int right = nums.size()-1;
int index = -1;
while(left <= right){
int middle = (left+right)/2;
if(nums[middle] > target){
right = middle-1;
}
else if(nums[middle] < target){
left = middle+1;
}
else{
index = middle;
break;
}
}
//这里证明没有找到元素,就要确定元素插入的位置
if(index == -1){
index = right+1;
}
return index;
}
};
leecode34. 在排序数组中查找元素的第一个和最后一个位置
今天的第二道题和别的题目有点不太一样,因为这次数组的元素虽然是按顺序排列但是是有重复的。
这就需要我们先找出左边界,再找出右边界
class Solution {
public:
int getLeft(vector<int>& nums, int target){
int left = 0;
int right = nums.size()-1;
int leftbord = -1;
while(left <= right){
int middle = (left+right)/2;
if(nums[middle] < target){
left = middle+1;
}
else{
//当找到target的值的时候,也让右边界往左边逼近,这样可以找到左边界
right = middle-1;
}
}
leftbord = right+1;
return leftbord;
}
int getRight(vector<int>& nums, int target){
int left = 0;
int right = nums.size()-1;
int rightbord = -1;
//让左届往右边逼近
while(left <= right){
int middle = (left+right)/2;
if(nums[middle] > target){
right = middle-1;
}
//最主要也是这一步,当target = nums[middle]时,也要让
//左边界向右移动,这样跳出循环时才可以满足找到右边界
else{
left = middle+1;
}
}
rightbord = left-1;
return rightbord;
}
vector<int> searchRange(vector<int>& nums, int target) {
//这题一看就是二分,但是有些不一样这个题目中的数组有重复元素
//那就遵循先找右边界再找左边界
int right = getRight(nums, target);
int left = getLeft(nums, target);
//这里还要进行一次判定才可以输出
if(right >= left) return{left, right};
else return {-1, -1};
}
};
leecode69.x的平方根
这道题第一次我用的是暴力解法。第一次被提示int可能储存不了那么多位。然后采用long来存储。因为经过我查看,他最后一次喂进去的数据是2147395600,虽然ii不超过int的最大储存,但是(i+1)(1+1)可能超过了。这个算法的算法复杂度是O(2N)。
class Solution {
public:
int mySqrt(int x) {
//因为用int类型会提示不够,所以用long long
long result = -1;
for(long i = 0; i <= x; i++){
if(x >= i*i && x < (i+1)*(i+1)){
result = i;
break;
}
}
return result;
}
};
然后也可以使用二分法来做这道题目
class Solution {
public:
int mySqrt(int x) {
//因为用int类型会提示不够,所以用long long
int left = 0;
int right = x;
int result = -1;
if(x == 1) result = 1;
else{
while((right-left) > 1){
long middle = (left+right)/2;
if(middle*middle <= x){
left = middle;
}
else if(middle*middle > x){
right = middle;
}
}
result = left;
}
return result;
}
};
写这个的时候遇到点小问题,还就是int不能储存的问题。还有1是特殊值。需要注意
class Solution {
public:
int mySqrt(int x)
{
if(x == 1)
return 1;
int left = 0;
int right = x;
while(right-left>1)
{
int m = (left+right)/2;
if(x/m < m)
right = m;
else if(m*m <= x)
left = m;
}
return left;
}
};
仔细看关于二分法解题时判断条件的问题。x/m < m不会报错,而mm > x则会报错。这时为什么呢?引用leecode上一位大神的解释。
如果mm > 2的31次方-1的话,会越界,比如输入x = 2147395599的时候,1073697799 * 1073697799就越界了。 mm > x 的话会先计算出m*m的值,再和x比较。所以这也是为什么第一种写法中如果用int来申明middle会发生如下错误
367. 有效的完全平方数
这道题就比较简单了,毕竟都做了这么多关于二分法的问题了
class Solution {
public:
bool isPerfectSquare(int num) {
if(num == 1) return true;
else{
int left = 0;
int right = num;
while(right - left > 1 ){
int middle = (left+right)/2;
if(middle > num/middle){
right = middle;
}
else{
left = middle;
}
}
if(left*left!=num) return false;
else return true;
}
}
};