二分法 Leetcode

二分法

前言

二分法的重点在于 搜索区间 ,常见的有 [left, right][left, right)。根据题目的不同选择不同的方法。

思路一:[left, right] 代表在循环体内部查找元素

  • while(left <= right) 这种写法表示在循环体内部直接查找元素;
  • 退出循环的时候 leftright 不重合,区间 [left, right] 是空区间。
  • 会对区间内的所有元素进行判断

思路二:[left, right) 代表在循环体内部排除元素

  • while(left < right) 这种写法表示在循环体内部排除元素;
  • 退出循环的时候 leftright 重合,区间 [left, right] 只剩下成 \(1\) 个元素,这个元素有可能就是我们要找的元素。
  • 「左右边界向中间走,两边夹」

一、简单的二分查找算法(leetcode 35、leetcode704)

思路一代码(闭区间)

int searchInsert(vector<int>& nums, int target) {
        int n = nums.size();
        int l = 0, r = n - 1; //注意,左闭右闭
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (target > nums[mid]) {
                l = mid + 1; 
            } else if (target < nums[mid]) {
                r = mid - 1; 
            } else if (target == nums[mid]) {
                return mid; 
            }
        }
        //return -1;
        return l;
    }

思路二代码(开区间)

int searchInsert(vector<int>& nums, int target) {
       	int n = nums.size();
        //if (target > nums[n - 1]) return -1;
        int l = 0, r = n; //注意,左闭右开
        while (l < r) {
            int mid = l + (r - l) / 2;
            if (target > nums[mid]) {
                l = mid + 1; 
            } else if (target < nums[mid]) {
                r = mid; //右开
            } else if (target == nums[mid]) {
                r = mid; //右开
            }
        }
        //return nums[l] == target ? l : - 1
        return l; //最终 l == r,代表目标位置或要插入的位置
    }

总结

对于最简单的二分查找来说,思路一比较简单,直接查找出 target 所在的位置,一旦跳出循环就意味着没有找到。但对于思路二来说,因为是左闭右开,对于下面这种情况(会产生数组越界访问)需要特判。

Input: nums = [-1,0,3,5,9,12], target = 13
Output: -1

二、寻找左侧边界的二分搜索

思路一代码

int left_bound(int[] nums, int target) {
	if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length;
    
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            right = mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid;
        }
    }
    return left;
}

思路二代码

int left_bound(int[] nums, int target) {
	if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            if (mid == 0 || nums[mid-1] != target) return mid;
            right = mid - 1;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        }
    }
    return left;
}

三、寻找右侧边界的二分搜索

思路一代码

int left_bound(int[] nums, int target) {
	if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length;
    
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            left = mid + 1;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid;
        }
    }
    return left - 1;
}

思路二代码

int left_bound(int[] nums, int target) {
	if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            if (mid == 0 || nums[mid-1] != target) return mid;
            right = mid - 1;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        }
    }
    return left;
}

四、寻找第一个和最后一个元素的位置(leetcode 34)

思路一代码

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> res;
        int n = nums.size();
        int l = 0, r = n;
        while (l < r) {
            int mid = l + (r - l) / 2;
            if (target < nums[mid]) {
                r = mid;
            } else if (target == nums[mid]) {
                r = mid;
            } else if (target > nums[mid]) {
                l = mid + 1;
            }
        }
        if (l == n || nums[l] != target) res.push_back(-1);
        else res.push_back(l);
        l = 0;
        r = n;
        while (l < r) {
            int mid = l + (r - l) / 2;
            if (target < nums[mid]) {
                r = mid;
            } else if (target == nums[mid]) {
                l = mid + 1;
            } else if (target > nums[mid]) {
                l = mid + 1;
            }
        }
        if (l == 0 || nums[l - 1] != target) res.push_back(-1);
        else res.push_back(l -1 );
        return res;
    }
};

思路二代码

class Solution {
   public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> res;
        int n = nums.size();
        int l = 0, r = n - 1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (target < nums[mid]) {
                r = mid - 1;
            } else if (target == nums[mid]) {
                if ((mid == 0) || (nums[mid - 1]) != target) {
                    res.push_back(mid);
                    break;
                } else
                    r = mid - 1;
            } else if (target > nums[mid]) {
                l = mid + 1;
            }
        }
        if (res.size() == 0) res.push_back(-1);
        l = 0;
        r = n - 1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (target < nums[mid]) {
                r = mid - 1;
            } else if (target == nums[mid]) {
                if ((mid == n - 1) || (nums[mid + 1]) != target) {
                    res.push_back(mid);
                    break;
                } else
                    l = mid + 1;
            } else if (target > nums[mid]) {
                l = mid + 1;
            }
        }
        if (res.size() == 1) res.push_back(-1);
        return res;
    }
};

五、寻找旋转排序数组的最小值

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

从左边考虑,应该使 mid 靠近 high

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

从右边考虑

class Solution {
public:
    int findMin(vector<int>& nums) {
        int n = nums.size();
        int l = 0, r = n - 1;
        while (l < r){
            int mid = l + (r - l) / 2;
            if (nums[mid] <= nums[r]) {
                r = mid;
            } else {
                l = mid + 1;
            }
        }
        return nums[l];
    }
};
posted @ 2021-02-19 16:00  snakeee  阅读(130)  评论(0)    收藏  举报