二分查找总结

二分搜索

二分查找也称折半查找 ( B i n a r y    S e a r c h ) (Binary \;Search) (BinarySearch),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
第一次写博客,之所以选这个算法是因为第一次面试问了这个题,自己学习的时候也没认真看,结果出现了一些问题,所有在这里总结一下。

递归解法

LEETCODE 35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
链接:leetcode-35 搜索插入位置

你可以假设数组中无重复元素。
在面试前,我遇到二分查找是直接用递归来写的,结果面试的时候面试官直接说别用递归,因为递归比较耗资源(函数栈)。但我还是先把递归解法写一下。

class Solution {
    public int searchInsert(int[] nums, int target) {
        if(target > nums[nums.length-1])
            return nums.length;
        return searchInsertHepler(nums, target, 0, nums.length - 1);
    }

    private int searchInsertHepler(int[] nums, int target, int start, int end){
        int mid;

        if(start == end){
            return start;
        }
        mid = (start + end) / 2;
        if(target > nums[mid])
            return searchInsertHepler(nums, target, mid + 1, end);
        return searchInsertHepler(nums, target, start, mid);
    }
}

递归解法时间复杂度 O ( l o g N ) O(logN) O(logN),空间复杂度 O ( l o g N ) O(logN) O(logN)。其实上述用的是尾递归(也是面试的时候才知道的),因此经过编译器优化后空间复杂度也是 O ( 1 ) O(1) O(1),但还是推荐用下面的迭代解法。

迭代解法

迭代解法可以将空间复杂度降到 O ( 1 ) O(1) O(1)

class Solution {
    public int searchInsert(int[] nums, int target) {
        int i, j, mid;

        if(nums.length == 0)
            return 0;
        i = 0;
        j = nums.length - 1;
        if(target > nums[j])
            return nums.length;
        while(j > i){
            mid = (i + j) / 2;
            if(target > nums[mid])
                i = mid + 1;
            else
                j = mid;
        }
        return i;
    }
}

关于二分查找的一些题目

在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 n u m s nums nums,和一个目标值 t a r g e t target target。找出给定目标值在数组中的开始位置和结束位置。

你的算法时间复杂度必须是 O ( l o g N ) O(logN) O(logN) 级别。

如果数组中不存在目标值,返回 [ − 1 , − 1 ] [-1, -1] [1,1]

链接:leetcode-34 在排序数组中查找元素的第一个和最后一个位置

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] answer;

        if(nums.length == 0)
            return new int[]{-1, -1};
        answer = new int[2];
        answer[0] = getMinIndex(nums, target);
        if(answer[0] == -1){
            answer[1] = -1;
        }else{
            answer[1] = getMaxIndex(nums, target);
        }
        return answer;
    }

    private int getMinIndex(int[] nums, int target){
        int i, j, mid;

        i = 0;
        j = nums.length - 1;
        while(j > i){
            mid = (i + j) / 2;
            if(target > nums[mid]){
                i = mid + 1;
            }else{
                j = mid;
            }
        }
        return nums[i] == target ? i : -1;
    }

    private int getMaxIndex(int[] nums, int target){
        int i, j, mid;

        i = 0;
        j = nums.length - 1;
        while(j > i){
            mid = (i + j + 1) / 2;
            if(target < nums[mid]){
                j = mid - 1;
            }else{
                i = mid;
            }
        }
        return i;
    }
}

这道题用了两次二分查找分别找出了数组中 t a r g e t target target 第一次和最后一次出现的位置,两个函数中不同的一个地方是用了

mid = (i + j) / 2;

mid = (i + j + 1) / 2;

区别是什么呢?当 [ i , j ] [i, j] [i,j] 的长度是奇数时,两种写法都是区最中间的那个数;不同的是当长度是偶数时,前者是取中间靠左的那个数,后者是取靠右的那个数。

搜索二维矩阵

编写一个高效的算法来判断 m × n m\times n m×n矩阵中,是否存在一个目标值。该矩阵具有如下特性:

每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
示例 1:
输入:
m a t r i x = [ 1 3 5 7 10 11 16 20 23 30 34 50 ] t a r g e t = 3 matrix = \left[ \begin{matrix} 1 & 3 & 5 & 7 \\ 10 & 11 &16&20 \\ 23&30&34&50\\ \end{matrix}\right]\\target = 3 matrix=11023311305163472050target=3
输出: t r u e true true
来源:leetcode-74

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int i, j, k, m, n, mid;
        
        m = matrix.length;
        if(m == 0)
            return false;
        n = matrix[0].length;
        if(n == 0)
            return false;
        if(matrix[0][0] > target)
            return false;
        i = 0;
        j = m - 1;
        while(j > i){
            mid = (i + j + 1) / 2;
            if(target < matrix[mid][0]){
                j = mid - 1;
            }else{
                i = mid;
            }
        }
        k = i;
        if(matrix[k][0] == target)
            return true;
        i = 0;
        j = n - 1;
        while(j > i){
            mid = (i + j + 1) / 2;
            if(target < matrix[k][mid]){
                j = mid - 1;
            }else{
                i = mid;
            }
        }
        return matrix[k][i] == target;
    }
}

两次二分查找,时间复杂度为 O ( l o g M + l o g N ) = O ( l o g ( M N ) ) O(logM+logN)=O(log(MN)) O(logM+logN)=O(log(MN))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值