Leetcode—二分

简单

第一个错误的版本

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<rmid,要么两者在峰顶同侧;要么rmid在峰顶右侧,lmid在峰顶左侧。即峰顶一定在lmid右侧。可令left = lmid;
反之,则令right = rmid。
有一点就是lmid和rmid的选取可以取三分点,这样的话依次分可以去掉1/3的区间;如果lmid和rmid取中点略微两侧的点,则一次可以去掉1/2的区间,这一种实现方法可以单独设一个mid = (right-left)/2+left, 另一指针设为mid+1即可。
还有一点要注意的使,如果return的是left,那就要让left去逼近峰顶,反之亦然;这体现在lmid=rmid时,究竟是left动还是right动上。

矩阵中战斗力最弱的k行

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用二分统计每一行的负数个数,即找到每一行的第一个负数。主函数做总的和即可。
可以看到时间成绩不高,我和李怀疑是我只用到了每行均为非递增,未用到每列也为非递增这一性质

第k个缺失的正整数

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即可。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值