二分查找
- x 的平方根
int mySqrt(int x) {
if (x <= 1)
return x;
int left = 0, right = x; // 防止left+right溢出, left为0而不是1
int mid;
while (left <= right) {
mid = (left + right) / 2;
if (x / mid == mid) {
return mid;
} else if (x / mid < mid) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return right;
}
- 在排序数组中查找元素的第一个和最后一个位置
解法1:先用二分法找到一个目标值,然后再往两边遍历(不满足时间复杂度为O(logN))
vector<int> searchRange(vector<int> &nums, int target) {
int low = 0, high = nums.size() - 1, mid;
int first = -1, last = -1;
while (low <= high) {
mid = (low + high) / 2;
if (nums[mid] == target) {
first = last = mid;
break;
} else if (nums[mid] > target) {
high = mid - 1;
} else {
low = mid + 1;
}
}
if (first == -1)return {-1, -1};
int tmp = first - 1;
while (tmp >= 0 && nums[tmp] == target) {
tmp--;
}
first = tmp + 1;
tmp = last + 1;
while (tmp < nums.size() && nums[tmp] == target) {
tmp++;
}
last = tmp - 1;
return {first, last};
}
解法2:用二分法分别查找目标值的左边界和右边界
int lower(vector<int>& nums, int target) {
if (nums.size() == 0)
return -1;
int left = 0, right = nums.size() - 1, mid;
while (left <= right) {
mid = (left + right) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
if (left >= nums.size() || nums[left] != target)
return -1;
return left;
}
int upper(vector<int>& nums, int target) {
if (nums.size() == 0)
return -1;
int left = 0, right = nums.size() - 1, mid;
while (left <= right) {
mid = (left + right) / 2;
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
if (right >= 0 && nums[right] == target)
return right;
return -1;
}
vector<int> searchRange(vector<int>& nums, int target) {
int first = lower(nums, target);
if (first == -1)
return {-1, -1};
int last = upper(nums, target);
return {first, last};
}
- 搜索旋转排序数组 II
bool search(vector<int>& nums, int target) {
int low = 0, high = nums.size() - 1, mid;
while (low <= high) {
mid = (low + high) / 2;
if (nums[mid] == target)
return true;
if (nums[mid] > nums[low]) {// 说明[low,mid]是排好序的
if (target >= nums[low] && target < nums[mid]) {
high = mid - 1;
} else {
low = mid + 1;
}
} else if (nums[mid] < nums[low]) {// 说明[mid,high]是排好序的
if (target > nums[mid] && target <= nums[high]) {
low = mid + 1;
} else {
high = mid - 1;
}
} else {// nums[mid]=nums[low],不能判断[low,mid]全部相同还是[mid,high]全部相同
low++;
}
}
return false;
}
- 寻找旋转排序数组中的最小值 II
解法1:暴力求解,找到第一个下降的元素即为最小的元素
int findMin(vector<int>& nums) {
int res = 0;
for (int i = 0; i < nums.size() - 1; ++i) {
if (nums[i] > nums[i + 1]) {
res = i + 1;
break;
}
}
return nums[res];
}
解法2:二分法,参考上一题 81. 搜索旋转排序数组 II
int findMin(vector<int>& nums) {
int low = 0, high = nums.size() - 1, mid;
while (low < high) {
if (low + 1 == high)
return min(nums[low], nums[high]);
mid = (low + high) / 2;
if (nums[mid] > nums[low]) {
if (nums[mid] <= nums[high]) {
return nums[low];
} else {
low = mid + 1;
}
} else if (nums[mid] < nums[low]) {
high = mid;
} else {
low++;
}
}
return nums[low];
}
解法3:解法2的变形,将nums[mid]和nums[high]进行比较来判断有序区间,
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];
}
- 有序数组中的单一元素
解法1:使用异或:x^0=x, x^x=0
int res = 0;
for (int i = 0; i < nums.size(); ++i) {
res ^= nums[i];
}
return res;
解法2:根据每对元素的下标变化来进行二分查找单一元素的下标(单一元素之前的每队元素的下标是偶奇偶奇,单一元素之后的每队元素的下标是奇偶奇偶)
int singleNonDuplicate(vector<int>& nums) {
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 (nums[mid] == nums[mid - 1]) {
if (mid % 2 == 1) {
low = mid + 1;
} else {
high = mid - 2;
}
} else {
if (mid % 2 == 0) {
low = mid + 2;
} else {
high = mid - 1;
}
}
}
return nums[low];
}
- 寻找两个正序数组的中位数
解法1:将两个数组合并后求中位数,略
解法2:已知两个正序数组长度分别是m和n,则中位数所在下标已确定(若m+n是奇数则中位数下标为(m+n)/2;若m+n是偶数则中位数下标为(m+n-1)/2和(m+n)/2),无需真正合并两个数组。
double findMedianSortedArrays2(vector<int> &nums1, vector<int> &nums2) {
int m = nums1.size(), n = nums2.size();
int index1 = 0, index2 = 0;
int left = 0, right = 0;
for (int i = 0; i <= (m + n) / 2; ++i) {
left = right;
if (index1 < m && (index2 >= n || nums1[index1] <= nums2[index2])) {
right = nums1[index1++];
} else {
right = nums2[index2++];
}
}
if ((m + n) % 2 == 0) {
return (left + right) / 2.0;
}
return right;
}
解法3:使用二分法查找两个有序数组中第K小的数
//根据两个有序数组的长度可以求出中位数的下标,因此这道题可以转化成求第k小的元素
//1.每次取nums1[k/2-1]和nums2[k/2-1]进行比较
//2.如果nums1[k/2-1]<nums2[k/2-1],则排除nums1[0]-nums1[k/2-1]这k/2个元素
//3.如果nums1[k/2-1]>nums2[k/2-1],则排除nums2[0]-nums2[k/2-1]这k/2个元素
//4.然后k=k-已排除元素的个数
int getKthMinElement(vector<int> &nums1, vector<int> &nums2, int k) {
int m = nums1.size(), n = nums2.size();
int index1 = 0, index2 = 0, newIndex1, newIndex2;
while (true) {
// 二分查找结束的三种情况: nums1已全部遍历/nums2已全部遍历/已确定k-1个元素
if (index1 == m) return nums2[index2 + k - 1];
if (index2 == n) return nums1[index1 + k - 1];
if (k == 1) return min(nums1[index1], nums2[index2]);
newIndex1 = min(index1 + k / 2 - 1, m - 1);
newIndex2 = min(index2 + k / 2 - 1, n - 1);
if (nums1[newIndex1] <= nums2[newIndex2]) {
k -= newIndex1 - index1 + 1;
index1 = newIndex1 + 1;
} else {
k -= newIndex2 - index2 + 1;
index2 = newIndex2 + 1;
}
}
}
double findMedianSortedArrays(vector<int> &nums1, vector<int> &nums2) {
int m = nums1.size(), n = nums2.size();
if ((m + n) % 2 == 1) {
return getKthMinElement(nums1, nums2, (m + n) / 2 + 1);
} else {
return (getKthMinElement(nums1, nums2, (m + n) / 2) + getKthMinElement(nums1, nums2, (m + n) / 2 + 1)) /
2.0;
}
}