文章目录
前言
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。
二分查找需要满足两个前提条件:
1)必须采用顺序存储结构。
2)元素按关键字有序排列。
关键需要明白查找的区间是"[]“还是”[)"; 以及查找的数据不等于目标元素时,目标元素所在的区间。
算法题
1. LeetCode 剑指 Offer 11 : 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
示例 2:
输入:[2,2,2,0,1]
输出:0
int minArray(vector<int>& numbers)
{
int left = 0;
int right = numbers.size()-1;
while(left < right)
{
int mid = left + (right - left)/2;
if(numbers[mid] < numbers[right]) {
right = mid;
} else if(numbers[mid] > numbers[right]) {
left = mid + 1;
} else {
right -= 1;
}
}
return numbers[left];
}
2. LeetCode 面试题 10.05 : 稀疏数组搜索
稀疏数组搜索。有个排好序的字符串数组,其中散布着一些空字符串,编写一种方法,找出给定字符串的位置。
示例1:
输入: words = [“at”, “”, “”, “”, “ball”, “”, “”, “car”, “”, “”,“dad”, “”, “”], s = “ta”
输出:-1
说明: 不存在返回-1。
示例2:
输入:words = [“at”, “”, “”, “”, “ball”, “”, “”, “car”, “”, “”,“dad”, “”, “”], s = “ball”
输出:4
提示:
words的长度在[1, 1000000]之间
/*
* 2
* LeetCode 面试题 10.05 : 稀疏数组搜索
* https://leetcode-cn.com/problems/sparse-array-search-lcci/
*/
int findString(vector<string>& words, string s)
{
int l = 0, r = words.size() - 1;
while(l < r)
{
int mid = (l + r) >> 1;
while(mid < r && words[mid] == "")
mid ++; //跳过空格
if(mid == r)
{
r = (l + r) >> 1;
continue;
} //若是跳过空格,mid == r, 则二分后赋值给r
if(words[mid] == s)
return mid; //标准二分法
else if(words[mid] < s)
l = mid + 1;
else
r = mid;
}
return words[r] == s ? r : -1;
}
3. LeetCode 378 : 有序矩阵中第K小的元素
给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素。
示例:
matrix = [
[ 1, 5, 9],
[10, 11, 13],
[12, 13, 15]
],
k = 8,
返回 13。
提示:
你可以假设 k 的值永远是有效的,1 ≤ k ≤ n2
/*
* 3
* LeetCode 378 : 有序矩阵中第K小的元素
* https://leetcode-cn.com/problems/kth-smallest-element-in-a-sorted-matrix/
*/
int kthSmallest(vector<vector<int>>& matrix, int k)
{
int n = matrix.size(),l = matrix[0][0], r = matrix[n-1][n-1];
int mid = l;
while(l < r)
{
mid=l + (r-l)/2;
int cnt = 0, cnt2 = 0;
for(int i = 0; i < n; i++)
{
auto &v = matrix[i];
cnt += lower_bound(v.begin(), v.end(),mid) - v.begin();
cnt2 += upper_bound(v.begin(), v.end(),mid) - v.begin();
}
if(cnt < k && cnt2 >= k)
return mid;
if(cnt<k)
l = mid+1;
else
r = mid;
}
return mid;
}
4. LeetCode 面试题 10.09 : 排序矩阵查找
给定M×N矩阵,每一行、每一列都按升序排列,请编写代码找出某元素。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
/*
* 4
* LeetCode 面试题 10.09 : 排序矩阵查找
* https://leetcode-cn.com/problems/sorted-matrix-search-lcci/
*/
bool searchMatrix(vector<vector<int>>& matrix, int target)
{
if(matrix.empty())
return false;
int row = 0;
int column = matrix[0].size() - 1;
while(row <= matrix.size() - 1 && column >= 0)
{
if(matrix[row][column] == target)
return true;
if(matrix[row][column] > target)
--column;
else
++row;
}
return false;
}
5. LeetCode 34 : 在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
/*
* 5
* LeetCode 34 : 在排序数组中查找元素的第一个和最后一个位置
* https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/
*/
vector<int> searchRange(vector<int>& nums, int target)
{
vector<int> res(2, -1);
if(nums.size() == 0)
return res;
//find front
int start = 0, end = nums.size() - 1;
while(start + 1 < end)
{
int mid = start + (end - start)/2;
if(nums[mid] >= target)
end = mid;
else
start = mid;
}
if(nums[start] == target)
res[0] = start;
else if(nums[end] == target)
res[0] = end;
//find back
start = 0; end = nums.size() - 1;
while(start + 1 < end)
{
int mid = start + (end - start)/2;
if(nums[mid] <= target)
start = mid;
else
end = mid;
}
if(nums[end] == target)
res[1] = end;
else if(nums[start] == target)
res[1] = start;
return res;
}
6. LeetCode 852 : 山脉数组的峰顶索引
我们把符合下列属性的数组 A 称作山脉:
A.length >= 3
存在 0 < i < A.length - 1 使得A[0] < A[1] < … A[i-1] < A[i] > A[i+1] > … > A[A.length - 1]
给定一个确定为山脉的数组,返回任何满足 A[0] < A[1] < … A[i-1] < A[i] > A[i+1] > … > A[A.length - 1] 的 i 的值。
示例 1:
输入:[0,1,0]
输出:1
示例 2:
输入:[0,2,1,0]
输出:1
提示:
3 <= A.length <= 10000
0 <= A[i] <= 10^6
A 是如上定义的山脉
/*
* 6
* LeetCode 852 : 山脉数组的峰顶索引
* https://leetcode-cn.com/problems/peak-index-in-a-mountain-array/
*/
int peakIndexInMountainArray(vector<int>& A)
{
assert(A.size() >= 3);
int left = 0;
int right = A.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(A[mid] < A[mid + 1])
left = mid + 1;
else
right = mid;
}
return left;
}
7. LeetCode 33 : 搜索旋转排序数组
给你一个升序排列的整数数组 nums ,和一个整数 target 。
假设按照升序排序的数组在预先未知的某个点上进行了旋转。(例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
请你在数组中搜索 target ,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:
输入:nums = [1], target = 0
输出:-1
提示:
1 <= nums.length <= 5000
-10^4 <= nums[i] <= 10^4
nums 中的每个值都 独一无二
nums 肯定会在某个点上旋转
-10^4 <= target <= 10^4
/*
* 7
* LeetCode 33 : 搜索旋转排序数组
* https://leetcode-cn.com/problems/search-in-rotated-sorted-array/
*/
int search(vector<int>& nums, int target)
{
if(nums.empty()) {
return -1;
}
int n = nums.size();
if(n == 1) {
return nums[0] == target ? 0 : -1;
}
int l = 0, r = n - 1;
while(l <= r)
{
int mid = l + (r-l)/2;
if(nums[mid] == target)
return mid;
if(nums[0] <= nums[mid]) {
if(nums[0] <= target && target < nums[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
} else {
if(nums[mid] < target && target <= nums[n - 1]) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
return -1;
}
8. LeetCode 704 : 二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
提示:
你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。
/*
* 8
* LeetCode 704 : 二分查找
* https://leetcode-cn.com/problems/binary-search/
*/
int search(vector<int>& nums, int target)
{
if(nums.empty())
return -1;
int start = 0, end = nums.size()-1;
while(start <= end)
{
int mid = start + (end-start)/2;
if(nums[mid] == target)
return mid;
else if(nums[mid] < target)
start = mid+1;
else
end = mid-1;
}
return -1;
}
模版
int binarySearch(vector<T>& nums, T target)
{
int left = 0, right = ...;
while(...)
{
int mid = left + (right - left) / 2;
if(nums[mid] == target) {
...
} else if(nums[mid] < target) {
left = ...
} else if(nums[mid] > target) {
right = ...
}
}
return ...;
}
以上是二分查找的算法模版。
总结
以上是二分查找相关面试算法题汇总,个别题目也给出了解题思路和注释。最后部分也给出了一个通用的模版,只需搞清楚了前言部分提到的两个问题:查找区间是闭区间还是开区间;当查找的数据不等于目标数据时应该如何缩小区间。所有代码都可以去我的网站GitHub查看,后续也将继续补充其他算法方面的相关题目。
博客介绍了二分查找,它是一种效率较高的查找方法,需满足顺序存储和元素有序两个条件。还列举了多道LeetCode算法题,如旋转数组最小数字、稀疏数组搜索等,最后给出算法模板,并表示可去GitHub查看代码,后续会补充其他算法题。
176万+

被折叠的 条评论
为什么被折叠?



