二分搜索总结

时间复杂度:

1)每次用O(1)的时间,将规模为n的问题变为n/2,则总时间复杂度为O(logn).

2)每次用O(n)的时间,将规模为n的问题变为n/2,则总时间复杂度为O(n).

因为T(n) = T(n/2) + O(n) = T(n/4) + O(n/2) + O(n) = .... = T(1) + O(2) + O(4) + ... + O(n/2) + O(n) 约等于 O(n)

2 + 4 + 8 +..+ n/2 + n = 2n-2

 

由时间复杂度可以反推会用到的算法:

 一. 找数组中第一个/最后一个 满足某个条件的位置:

1. 第一次出现的下标:

思路:使用二分法逐渐把范围缩小,最后start和end指向两个相邻的元素。再进行double check。

class Solution {
public:
    /**
     * @param nums: The integer array.
     * @param target: Target to find.
     * @return: The first position of target. Position starts from 0.
     */
    int binarySearch(vector<int> &nums, int target) {
        // write your code here
        if(nums.empty())
            return -1;   //target不存在数组中,返回-1
        int start = 0, end = nums.size()-1;
        
        //相邻就退出循环
        // start = 1, end = 2 就要退出
        while(start + 1 < end){
            int mid = start + (end-start)/2;
            if(target == nums[mid])
                end = mid;
            else if(target < nums[mid])
            //也可写成 end = mid-1;
                end = mid;
            else
            //或 start = mid+1;
                start = mid;
        }
        //double check
        if(nums[start] == target)
            return start;
        if(nums[end] == target)
            return end;
        //未找到    
        return -1;
    }
};

2. 最后一次出现的下标:

 

 

 注意while循环的判断条件一定要写成:

start+1 < end

因为写成start<end时会导致死循环。并注意在double check时,要先check nums[end], 再check nums[start], 因为是找最后一个满足条件的。

 
 
class Solution {
public:
    /**
     * @param nums: An integer array sorted in ascending order
     * @param target: An integer
     * @return: An integer
     */
    int lastPosition(vector<int> &nums, int target) {
        // write your code here
        //start<end => dead loop
        //e.g. [2,2], target = 2, mid=(0+1)/2=0
        //then start = mid = 0, always < end
        if(nums.empty())
            return -1;
        int start = 0, end = nums.size()-1;
        while(start+1 < end){
            int mid = start + (end-start)/2;
            if(nums[mid] == target)
                start = mid;
            else if(target > nums[mid])
                start = mid;
            else
                end = mid;
        }
        if(nums[end] == target)
            return end;
        if(nums[start] == target)
            return start;
        return -1;
    }
};

 3. 找到某个数在数组中出现的坐标区间:

注意 if else 要写全...

class Solution {
public:
    /**
     * @param A: an integer sorted array
     * @param target: an integer to be inserted
     * @return: a list of length 2, [index1, index2]
     */
    vector<int> searchRange(vector<int> &A, int target) {
        // write your code here
        int n = A.size();
        vector<int> res(2, -1);
        if(A.empty())
            return res;
        
        int start = 0, end = n-1;
        while(start+1<end){
            int mid = start + (end-start)/2;
            if(A[mid] == target)
                end = mid;  //找target的起始坐标
            else if(target < A[mid])
                end = mid;
            else
                start = mid;
        }
        if(A[start] == target)
            res[0] = start;
        else if(A[end] == target)
            res[0] = end;
        
        start = 0, end = n-1;
        while(start+1<end){
            int mid = start + (end-start)/2;
            if(A[mid] == target)
                start = mid;  //找target的结尾坐标
            else if(target < A[mid])
                end = mid;
            else
                start = mid;
        }
        if(A[end] == target)
            res[1] = end;
        else if(A[start] == target)
            res[1] = start;
        
        return res;
    }
};

  

3. 找到target在数组中插入的位置:即如果找到相等的值就插入到这个值所在的索引;找到第一个比target大的值,就插入到这个值的位置上,代码是找到最后一个比target小的位置,然后将target插入到这个位置+1处。

 

class Solution {
public:
    /**
     * @param A: an integer sorted array
     * @param target: an integer to be inserted
     * @return: An integer
     */
    int searchInsert(vector<int> &A, int target) {
        // write your code here
        //找到第一个大于等于target的位置
        if(A.empty())
            return 0;
        if(target<A[0])
            return 0;
            
        int start = 0, end = A.size()-1;
        //找到最后一个小于等于target的位置
        while(start+1 < end){
            int mid = start + (end-start)/2;
            if(A[mid] == target)
                return mid;
            else if(target < A[mid])
                end = mid;
            else 
                start = mid;
        }
        if(A[end] == target)
            return end;
        else if(A[end] < target)
            return end+1;
        if(A[start] == target)
            return start;
        else if(A[start] < target)
            return start+1;
    }
};

 

二分法:

class Solution {
public:
    /**
     * @param matrix: matrix, a list of lists of integers
     * @param target: An integer
     * @return: a boolean, indicate whether matrix contains target
     */
    bool searchMatrix(vector<vector<int>> &matrix, int target) {
        // write your code here
        if(matrix.empty() || matrix[0].empty())
            return false;
        int n = matrix.size(), m = matrix[0].size();
        int start = 0, end = n*m-1;
        int rol=0, col = 0;
        while(start+1 < end){
            int mid = start + (end-start)/2;
            rol= mid/m, col = mid%m;
            if(matrix[rol][col] == target)
                return true;
            else if(target < matrix[rol][col])
                end = mid;
            else
                start = mid;
        }
        
        if(matrix[start/m][start%m] == target || matrix[end/m][end%m] == target)
            return true;
        return false;
    }
    
};

 

思路:从矩阵的左下角出发,每次去掉一行或一列,因为是统计矩阵中等于target的次数,所以遇到相等的数值,需要将res+1,然后再将i-1和j+1继续寻找。

class Solution {
public:
    /**
     * @param matrix: A list of lists of integers
     * @param target: An integer you want to search in matrix
     * @return: An integer indicate the total occurrence of target in the given matrix
     */
    int searchMatrix(vector<vector<int>> &matrix, int target) {
        // write your code here
        //从左下角出发,每次删除一行或一列
        if(matrix.empty() || matrix[0].empty())
            return 0;
        int n = matrix.size(), m = matrix[0].size();
        int res = 0;
        int i=n-1, j = 0;
        while(i>=0 && j<=m-1){
            if(matrix[i][j] == target){
                res ++;
                i--;
                j++;
            }
                
            else if(target < matrix[i][j]){
                i--;
            }
            else 
                j++;
        }
        return res;
    }
};

 

// Forward declaration of isBadVersion API.
bool isBadVersion(int version);

class Solution {
public:
    int firstBadVersion(int n) {
        //找到第一个bad version
        int start = 0, end = n;
        while(start+1 < end){
            int mid = start + (end-start)/2;
            if(isBadVersion(mid))
                end = mid;
            else
                start = mid;
        }
        if(isBadVersion(start))
            return start;
        return end;
    }
};

 

 

class Solution {
public:
    int peakIndexInMountainArray(vector<int>& A) {
        int start = 0, end = A.size()-1;
        while(start+1 < end){
            int mid = start + (end-start)/2;
            if(A[mid] < A[mid+1])
                start = mid;
            else
                end = mid;
        }
        if(A[start]>A[end])
            return start;
        else
            return end;
    }
};

进阶版:

解法一:O(n) 线性扫描一遍。每次只需要比较

 nums[i]>nums[i+1]

分三种情况:

 第二种情况是递增的数组,最后返回的是数组的最后一个元素,即nums.size()-1

 

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int n = nums.size();
        for(int i=0; i<n-1; i++){
             if(nums[i]>nums[i+1])
                 return i;
         }
        return n-1;
    }
};

 

解法二:二分法

 

 

 

 

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int start = 0, stop = nums.size()-1;
        while(start+1 < stop){
            int mid = start + (stop-start)/2;
            if(nums[mid] < nums[mid+1])
                start = mid;
            else
                stop = mid;
        }
        if(nums[start]>nums[stop])
            return start;
        else
            return stop;
    }
};

 

 

二分法:可以将这个旋转数组看作两段递增的数组,然后多加一层判断。

 

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.empty())
            return -1;
        int start = 0, end = nums.size()-1;
        while(start+1 < end){
            int mid = start + (end-start)/2;
            if(nums[mid] == target)
                return mid;
            if(nums[start] < nums[mid]){
                //在左半段
                if(target >= nums[start] && target <= nums[mid])
                    end = mid;
                else
                    start = mid;
            }
            else{
                //在右半段
                if(target >= nums[mid] && target <= nums[end])
                    start = mid;
                else
                    end = mid;
            }
        }
        if(nums[start] == target)
            return start;
        else if(nums[end] == target)
            return end;
        return -1;
    }
};

进阶版:旋转数组中有重复元素,时间复杂度只能是O(n)。

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        if(nums.empty())
            return false;
        int start = 0, end = nums.size()-1;
        while(start + 1 < end){
            int mid = start + (end-start)/2 ;
            if(nums[mid] == target)
                return true;
            if(nums[start] < nums[mid]){
                if(target<=nums[mid] && target>= nums[start])
                    end = mid;
                else
                    start = mid;
            }
            else if(nums[start] > nums[mid]){
                if(target>=nums[mid] && target<=nums[end])
                    start= mid;
                else
                    end = mid;
            }
            else 
                //nums[start] > nums[mid]
                start ++ ;
        }
        if(nums[start] == target || nums[end] == target)
            return true;
        return false;
    }
};

 

 

参考思路:https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/solution/

 

class Solution {
public:
    int findMin(vector<int>& nums) {
        if(nums.size()==1)
            return nums[0];
        
        int start = 0, end = nums.size()-1;
        if(nums[end] > nums[0])
            //说明没有反转,本身是一个排序好的数组
            return nums[0];
        
        while(start + 1 < end){
            int mid = start + (end-start)/2 ;
            if(nums[mid] < nums[mid-1])
                return nums[mid];
            if(nums[mid] > nums[mid+1])
                return nums[mid+1];
            if(nums[mid] > nums[0])
                start = mid;
            else
                end = mid;
        
        }
        if(nums[start]>nums[end])
            return nums[end];
        else
            return nums[start];
 
    }
};

 

二分+分治:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int len = nums1.size() + nums2.size();
        if(len % 2 == 0)
            //长度为偶数 中位数为中间两个数的平均值
            return (findKth(nums1, 0, nums2, 0, len/2) + findKth(nums1, 0, nums2, 0, len/2 +1))/2.0;
        else
            return findKth(nums1, 0, nums2, 0, len/2 +1);
    }
    int findKth(vector<int>& n1, int start1, vector<int>& n2, int start2, int k){
        //递归终止条件
        if(start1 >= n1.size())
            return n2[start2 + k -1];
        if(start2 >= n2.size())
            return n1[start1 + k -1];
        if(k==1)
            return min(n1[start1], n2[start2]);
        
        //若k/2的长度不在nums1中,则将其k/2处设为最大值,表示只会删掉/挪动start2的start2指针
        //取n1的第k/2个数
        int halfK1 = start1 + k/2 -1 < n1.size() ? n1[start1 + k/2 -1] : INT_MAX;
        //取n2的第k/2个数
        int halfK2 = start2 + k/2 -1 < n2.size() ? n2[start2 + k/2 -1] : INT_MAX;
        
        if(halfK1 < halfK2)
            return findKth(n1, start1 + k/2, n2, start2, k - k/2);
        else
            return findKth(n1, start1, n2, start2 + k/2, k- k/2);
    }
    
};

 

思路:三步翻转法:先把前一段翻转,再把后一段翻转,再把整个数组翻转。

class Solution {
public:
    /**
     * @param nums: An integer array
     * @return: nothing
     */
    void recoverRotatedSortedArray(vector<int> &nums) {
        // write your code here
        
        for(int i=0; i<nums.size()-1; i++){
            if(nums[i] > nums[i+1]){
                //找到一个比后面一位大的数
                reverse(nums.begin(), nums.begin()+i+1);  //reverse是左闭右开区间
                reverse(nums.begin()+i+1, nums.end());
                reverse(nums.begin(), nums.end());
            }
        }
    }
};

 

 

class Solution {
public:
    /**
     * @param str: An array of char
     * @param offset: An integer
     * @return: nothing
     */
    void rotateString(string &str, int offset) {
        // write your code here
        int n = str.size();
        if(n==0)
            return;
        offset %= n;
        reverse(str.begin(), str.begin()+n-offset);
        reverse(str.begin()+n-offset, str.end());
        reverse(str.begin(), str.end());
    }
};

 

class Solution {
public:
    /**
     * @param str: An array of char
     * @param offset: An integer
     * @return: nothing
     */
    void rotateString(string &str, int offset) {
        // write your code here
        int n = str.size();
        if(n==0)
            return;
        offset %= n;
        str = str.substr(n-offset, offset) + str.substr(0, n-offset);  //substr(pos, len)
    }
};

 

使用O(1)的空间复杂度,思路:先将整个字符串s翻转,处理掉首尾的空格后,再将每个单词翻转,再处理中间的冗余空格。

class Solution {
public:
    string reverseWords(string s) {
        //先整体翻转,再逐个单词翻转,需要处理首尾及中间冗余空格
        reverse(s.begin(), s.end());
        int n = s.size();
        int start=0, end = n-1;
        //首空格
        while(start<n && s[start]==' ')
            start++;
        //尾空格
        while(end>=0 && s[end]==' ')
            end--;
        //特殊情况
        if(start > end)
            return "";
        
        //逐个单词翻转
        for(int i=start; i<=end; ){
            while(s[i]==' ' && i<=end)
                i++;
            int l = i;
            while(s[l]!=' ' && l<=end)
                l++;
            reverse(s.begin()+i, s.begin()+l);
            i = l;
        }
        
        //处理中间冗余空格
        int tail = start;
        for(int i=start; i<=end; i++){
            if(s[i] == ' ' && s[i-1] == ' ') //去掉重复的空格
                continue;
            s[tail++] = s[i];
        }
        
        return s.substr(start, tail-start);  //s.substr(pos, len)
    }
};

 

 

 

class Solution {
public:
    bool rotateString(string A, string B) {
        //find()函数在找不到指定值得情况下会返回string::npos
        return A.size()==B.size() && (A+A).find(B) != string::npos;  //把两个A字符串拼起来,若在里面找不到B返回false
    }
};

 

转载于:https://www.cnblogs.com/Bella2017/p/11367837.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值