二分查找相关面试算法题总结

博客介绍了二分查找,它是一种效率较高的查找方法,需满足顺序存储和元素有序两个条件。还列举了多道LeetCode算法题,如旋转数组最小数字、稀疏数组搜索等,最后给出算法模板,并表示可去GitHub查看代码,后续会补充其他算法题。

前言

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。

二分查找需要满足两个前提条件:
1)必须采用顺序存储结构。
2)元素按关键字有序排列。

关键需要明白查找的区间是"[]“还是”[)"; 以及查找的数据不等于目标元素时,目标元素所在的区间。

算法题

1. LeetCode 剑指 Offer 11 : 旋转数组的最小数字

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 : 稀疏数组搜索

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小的元素

LeetCode 378

给定一个 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 : 排序矩阵查找

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 : 在排序数组中查找元素的第一个和最后一个位置

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 : 山脉数组的峰顶索引

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 : 搜索旋转排序数组

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 : 二分查找

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查看,后续也将继续补充其他算法方面的相关题目。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值