leetcode直达
// The API isBadVersion is defined for you.
// bool isBadVersion(int version);
class Solution {
public:
int binary(int left, int right){
while(left<=right){
int mid = (right-left)/2+left;
if(isBadVersion(mid) != isBadVersion(mid - 1)) return mid;
else if(isBadVersion(mid)) right = mid;
else if(!isBadVersion(mid)) left = mid+1;
}
return 9;
}
int firstBadVersion(int n) {
if(isBadVersion(1)) return 1;
return binary(1, n);
}
};
成绩:
思路:
抓住前半部分是false,后半部分是true的特点。
在基本的二分查找的基础上,改变判断条件。
当isBadVersion(mid) != isBadVersion(mid - 1)
,即当前版本为对,前一版本为错,即找到我们需要的版本。
当isBadVersion(mid)
,即当前版本为错,且前一版本也为错,就搜索前半部分的版本;
当!isBadVersion(mid)
,即当前版本为对,且前一版本也为对,就搜索后半部分的版本;
还有一点:int mid = (right-left)/2+left;
相对于int mid = (left+right)/2;
,本身是没有区别的,但是涉及到int就不一样了,前者能避免越界。
leetcode直达
思路一:调用sqrt
class Solution {
public:
bool isPerfectSquare(int num) {
int tmp = sqrt(num);
if(pow(tmp, 2) == num) return true;
else return false;
}
};
成绩:100 68
思路:
这个就没啥好说的,顾码思义。就看num开平方是不是个自然数。
没有用到二分的思想。
思路二:二分找区间
class Solution {
public:
bool isPerfectSquare(int num) {
if(num == 1) return true;
int left = 0;
int right = num;
while(right-left>1){
int mid = (left+right)/2;
if(pow(mid, 2) == num) return true;
else if(pow(mid, 2) < num) left = mid;
else if(pow(mid, 2) > num) right = mid;
}
return false;
}
};
成绩:100 96
思路:
我们都直到,只要一个数不是完全平方数,它的开方就必然在一个长度为1的区间内。比如30的开方就必然在[5,6]内。我们通过二分,就必然能找到对应区间。
具体二分过程看代码即可,没啥难度。
/**
* Forward declaration of guess API.
* @param num your guess
* @return -1 if num is lower than the guess number
* 1 if num is higher than the guess number
* otherwise return 0
* int guess(int num);
*/
class Solution {
public:
int guessNumber(int n) {
if(n == 1) return 1;
int left = 0;
int right = n;
while(left<=right){
int mid = (right-left)/2+left;
if(!guess(mid)) return mid;
else if(guess(mid) == -1) right = mid-1;
else if(guess(mid) == 1) left = mid+1;
}
return 8;
}
};
成绩:100 62.39
思路:
就是最简单的二分。
class Solution {
public:
long long sum(int mid){
long long tmp = (long long)mid*(mid+1)/(long long)2;
return tmp;
}
int arrangeCoins(int n) {
int left = 1;
int right = n;
while(right-left>1){
int mid = (right-left)/2+left;
if(sum(mid)<=(long long)n) left = mid;
else right = mid;
}
return left;
}
};
成绩:100 5.98
思路:
根据k*(k+1)/2这个公式二分就行。
真正让我值得注意的是边界的选择和int溢出这些问题。二分确实非常注重细节。
leetcode直达
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size()-1;
while(right>=left){
int mid = (right-left)/2+left;
if(nums[mid] == target) return mid;
else if(nums[mid]<target) left = mid+1;
else right = mid-1;
}
return -1;
}
};
成绩:96 23
思路:
就是最普通的二分查找,但是边界的选择需要整理一下,有两种选择的方式,明天早八下课回来写。
leetcode直达
class Solution {
public:
char nextGreatestLetter(vector<char>& letters, char target) {
int left = 0;
int right = letters.size()-1;
if(target>=letters[right] || target<letters[0]) return letters[0];
while(left<right){
int mid = (right-left)/2+left;
if(target>=letters[mid]) left = mid+1;
else right = mid;
}
return letters[right];
}
};
成绩:58 53
思路:
其他没什么好讲的,讲讲集中需要特殊处理的情况把:
1、当target比letters第一个字符小时,返回第一个字符;
2、当target比letters最后一个字符大或等于最后一个字符时,返回第一个字符;
还有就是这题需要返回的是比目标数字大的最小字符,所以当target==letters[mid]
时,仍需往后查找。
例如下面的例子就需要返回n;
[“a”,“a”,“a”, “a”,“a”,“a”,“a”, “n”,“n”]
“a”
leetcode直达
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr) {
int len = arr.size();
int left = 0;
int right = len-1;
int lmid = 0;
int rmid = 0;
while(right-left>1){
lmid = left+(right-left)/3;
rmid = left+(right-left)*2/3;
if(arr[lmid]<=arr[rmid]) left = lmid+1;
else right = rmid;
}
return left;
}
};
成绩:39.94 30.71
思路:
俗称三分,在[left,right]内取两个点lmid,rmid。
若lmid
有一点就是lmid和rmid的选取可以取三分点,这样的话依次分可以去掉1/3的区间;如果lmid和rmid取中点略微两侧的点,则一次可以去掉1/2的区间,这一种实现方法可以单独设一个mid = (right-left)/2+left
, 另一指针设为mid+1即可。
还有一点要注意的使,如果return的是left,那就要让left去逼近峰顶,反之亦然;这体现在lmid=rmid
时,究竟是left动还是right动上。
leetcode直达
class Solution {
public:
int count(vector<int> &mat){
int sum = 0;
while(sum<mat.size() && mat[sum]) sum++;
return sum;
}
vector<int> kWeakestRows(vector<vector<int>>& mat, int k) {
vector<vector<int>> res;
for(int i = 0;i<mat.size();i++){
res.push_back({count(mat[i]),i});
}
sort(res.begin(), res.end());
vector<int> resc;
int n = 0;
while(n<k){
resc.push_back(res[n][1]);
n++;
}
return resc;
}
};
成绩:73 12
思路:
瞟一眼代码就能看出这个方法没用二分了。这题里能用二分的点就是去找每一行的最后一个军人(1)或第一个平民(0),这样就能统计除每支队伍里的军人的数量了。那我没用二分我咋做的呢,我直接统计了,遍历到0就结束。你说二分行不行,那太行了,你说直接遍历统计香不香,那实在是太香了。
其他的就没什么好说的,搞个二维数组存每一行军人数量和序号,再排序输出需要的行数即可。
leetcode直达
class Solution {
public:
int count(vector<int>& grid){
int left = 0;
int right = grid.size() - 1;
int len = grid.size();
while (left <= right) {
int mid = (right - left) / 2 + left;
if(mid == 0 && grid[mid]<0) return len;
if (grid[mid] < 0 && grid[mid - 1] >= 0) return len - mid;
else if (grid[mid]<0 && grid[mid - 1] < 0) right = mid - 1;
else left = mid + 1;
}
return 0;
}
int countNegatives(vector<vector<int>>& grid) {
int sum = 0;
vector<vector<int>>::iterator it;
for(it = grid.begin();it<grid.end();it++){
sum+=count(*it);
}
return sum;
}
};
成绩:57.83 84.03
思路:
辅助函数count用二分统计每一行的负数个数,即找到每一行的第一个负数。主函数做总的和即可。
可以看到时间成绩不高,我和李怀疑是我只用到了每行均为非递增,未用到每列也为非递增这一性质
leetcode直达
class Solution {
public:
int findKthPositive(vector<int>& arr, int k) {
int n = 1;
while(k){
if(!count(arr.begin(),arr.end(),n)) k--;
n++;
}
return n-1;
}
};
成绩:8.38 76.61
思路:
无二分,看了看官方题解的二分,一下子没看懂,以后有空了再去研究研究。
从1开始遍历,如果遍历到的数不在arr里,说明是缺失的,那么k就减一。直到遍历到k为0,说明当前数就是我们要找的数。
添加链接描述
class Solution {
public:
int search(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
if (target < nums[0]) return nums.size();
if (target > nums.back()) return 0;
int left = 0;
int right = nums.size() - 1;
int mid = 0;
while (right >= left) {
mid = (right - left) / 2 + left;
if (nums[mid] >= target) right = mid - 1;
else left = mid + 1;
}
return nums.size()-right-1;
}
int specialArray(vector<int>& nums) {
int max = *max_element(nums.begin(),nums.end());
int left = 0;
int right =max;
while(left<=right){
int mid = (right-left)/2+left;
if(search(nums,mid)==mid) return mid;
else if(search(nums,mid)>mid) left = mid+1;
else right = mid-1;
}
return -1;
}
};
成绩:68.42 63.16
思路:
这题写的有点爽。
辅助函数search找到target所处的区间,返回大于等于target的元素的个数。反映到二分就是查找第一个大于等于target的数的位置。
主函数二分找特征数。二分区间取0~nums.max。利用辅助函数,当search(nums,mid) == mid,即找到了,返回。反之则说明没有符合题意的特征值,返回-1。
添加链接描述
class Solution {
public:
vector<int> fairCandySwap(vector<int>& aliceSizes, vector<int>& bobSizes) {
int aliceSum = accumulate(aliceSizes.begin(), aliceSizes.end(),0);
int bobSum = accumulate(bobSizes.begin(), bobSizes.end(),0);
vector<int> res;
for(vector<int>::iterator it = bobSizes.begin();it<bobSizes.end();it++){
int x = (aliceSum-bobSum+2**it)/2;
if(count(aliceSizes.begin(),aliceSizes.end(),x)){
res.push_back(x);
res.push_back(*it);
break;
}
}
return res;
}
};
成绩:5.8 89.47
思路:
本方法无二分。
我们设alice给bob的棒糖大小为x,bob给alice的棒糖大小为y。可以得到aliceSum-x+y = bobSum-y+x
;化简后即可得到x = (aliceSum-bobSum+2y)/2
。可以遍历bobSizes作为y,在aliceSizes中查找通过计算得到的x。当找到符合条件的x时,就return即可。