作者学习算法的教材是LeetCode 101
有需要的同学直接GitHub搜索LeetCode 101即可
**
居合斩!二分查找
69. x的平方根(难度:简单)
- 二分查找;
- 代码如下
class Solution {
public:
int mySqrt(int x) {
int l = 0, r = x, ans = -1;
while (l <= r) {
int mid = l + (r - l) / 2;
if ((long long)mid * mid <= x) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ans;
}
};
34.在排序数组中查找元素的第一个和最后一个位置(难度:中等)
- 二分查找;
- 分别寻找左右边界;
- 代码如下:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int leftBorder = getLeftBorder(nums, target);
int rightBorder = getRightBorder(nums, target);
// 情况一
if (leftBorder == -2 || rightBorder == -2) return {-1, -1};
// 情况三
if (rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
// 情况二
return {-1, -1};
}
private:
int getRightBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
while (left <= right) {
int middle = left + ((right - left) / 2);
if (nums[middle] > target) {
right = middle - 1;
} else { // 寻找右边界,nums[middle] == target的时候更新left
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
}
int getLeftBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
while (left <= right) {
int middle = left + ((right - left) / 2);
if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新right
right = middle - 1;
leftBorder = right;
} else {
left = middle + 1;
}
}
return leftBorder;
}
};
35.搜索插入位置(难度:简单)
- 二分查找;
- 只需寻找左边界,然后结果加一即可;
- 代码如下:
class Solution {
public:
int getLeftBorder(vector<int>& nums,int target) {
int l = 0,r = nums.size() - 1,mid,leftBorder = -1;
while (l <= r) {
mid = (l + r)/2;
if (nums[mid] >= target) {
r = mid - 1;
leftBorder = r;
}
else {
l = mid + 1;
}
}
return leftBorder;
}
int searchInsert(vector<int>& nums, int target) {
if (target > nums[nums.size()-1]) {
return nums.size();
}
else if (target < nums[0]) {
return 0;
}
else {
return getLeftBorder(nums,target) + 1;
}
}
};
81.搜索旋转排序数组Ⅱ(难度:中等)
- 二分查找;
- 自己写的,排序之后正常二分查找(偷懒了偷懒了);
- 代码如下:
class Solution {
public:
bool search(vector<int>& nums, int target) {
sort(nums.begin() , nums.end());
int low = 0 , high = nums.size() - 1 , mid;
while (low <= high) {
mid = (low + high) / 2;
if (nums[mid] > target) {
high = mid - 1;
}
else if (nums[mid] < target) {
low = mid + 1;
}
else if (nums[mid] == target) {
return true;
}
}
return false;
}
};
- 找出左右有序区间后二分查找;
- 基本思想是,如果当前中点小于等于右端点,说明右侧有序,反之说明左侧有序;如果目标值在有序区间内,则对此区间二分查找,反之对另一半区间二分查找;如果遇到中点值等于左端点值,无法判断左侧有序还是右侧有序时,则将左端点值加一,排除一个干扰项(如10111 和 1110111101 );
- 代码如下
class Solution {
public:
bool search(vector<int>& nums, int target) {
int low = 0 , end = nums.size() - 1;
while (low <= end) {
int mid = (low + end) / 2;
if (nums[mid] == target) {
return true;
}
if (nums[low] == nums[mid]) {
low++;
}
else if (nums[mid] <= nums[end]) {
target > nums[mid] && target <= nums[end] ? low = mid + 1 : end = mid - 1;
}
else {
target >= nums[low] && target < nums[mid] ? end = mid - 1 : low = mid + 1;
}
}
return false;
}
};
154. 寻找旋转排序数组中的最小值 II(难度:困难)
- 二分查找;
- 首先是常规的二分查找,然后判断条件不同,若中点比右端点大,说明最小值在中点右侧,所以low = mid + 1,反之在左侧,high = mid,如果中点等于右端点,无法确定最小值在左侧还是右侧(如1111011),于是右端点前移,high - 1;
- 代码如下:
class Solution {
public:
int findMin(vector<int>& nums) {
int low = 0 , high = nums.size() - 1 , mid;
while (low <= high) {
mid = (low + high) / 2;
if (nums[mid] > nums[high]) {
low = mid + 1;
}
else if (nums[mid] < nums[high]) {
high = mid;
}
else {
high--;
}
}
return nums[low];
}
};
540. 有序数组中的单一元素(难度:中等)
- 思路简单,找到中点后判断是否出现两次,若不是,则返回,若是,判断除中点和与其相同的点外,左右两边哪边为单数,为单数的一边一定有单一元素;
- 关键点在于只有三个元素时如何处理,只有三个元素时,判断中点和左右两个点哪个相同,返回不同点;
if (nums[mid] == nums[mid - 1]) return nums[high]; else return nums[low];
该语句我本来也想变为三目运算符,提高简洁性,但是由于判断条件是==,不满足三目运算符的性质(表达式?表达式:表达式;),所以报错,只能改为if else形式(我理解的是==不属于表达式形式,如果是我理解有误,欢迎大佬指正指正);- 代码如下:
class Solution {
public:
int singleNonDuplicate(vector<int>& nums) {
if (nums.size() == 1) {
return nums[0];
}
int low = 0 , high = nums.size()-1 , mid;
while (low <= high) {
mid = (low + high) / 2;
if (nums[mid] != nums[mid + 1] && nums[mid] != nums[mid + -1]) {
return nums[mid];
}
if (2 == (high - low)) {
if (nums[mid] == nums[mid - 1])
return nums[high];
else
return nums[low];
}
if (nums[mid] == nums[mid + 1]) {
(high - (mid + 1)) % 2 == 0 ? high = mid - 1 : low = mid;
}
else if (nums[mid] == nums[mid - 1]) {
(high - mid) % 2 == 0 ? high = mid : low = mid + 1;
}
}
return 0;
}
};
4. 寻找两个正序数组的中位数(难度:困难)
- 归并排序后找中位数;
- 代码如下:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int i = 0;
while (i < nums2.size()) {
nums1.push_back(nums2[i]);
i++;
}
sort(nums1.begin() , nums1.end());
int low = 0 , high = nums1.size() - 1;
int mid = (low + high) / 2;
if ((low + high) % 2 == 0) {
return nums1[mid];
}
else {
double mid1 = nums1[mid];
double mid2 = nums1[mid + 1];
return (mid1 + mid2) / 2;
}
return 0;
}
};
- 进阶难度:时间复杂度为O(log(m+n))
- 方法:找第k小元素
- 代码如下:
在这里插入代码片