其他分享
首页 > 其他分享> > leetcode奇技淫巧-吃透“复杂”的二分查找

leetcode奇技淫巧-吃透“复杂”的二分查找

作者:互联网

 

文章目录

 

二分查找很简单?

简单?

虽然大家都说二分查找很简单,很基础,其实我一直觉得它并不简单,实际上如果是一个新手写二分查找,他会发现其中有非常多的情况要考虑到。那为什么我们把二分查找的题目写多了之后就觉得写二分查找很简单了呢?这是因为我们人脑比较容易记住模型场景,记住定式,虽然我们在刷 leetcode 的时候,题目中在二分的时候,各种情况我们不一定都去想过一遍,但是什么样的场景该用什么样的模型我们知道(刷题训练的结果),所以我们能很快写出来。所以我一直认为那些能力一般却总是说“简单”的人,连为什么“简单”这个本质都没有看清,这样的思维能力对于他们怎么可能简单呢?其实也只是不断训练得到的结果,当然并不是说训练不重要,训练固然重要,如果我们带有自己的总结和思考去刷那些并不简单的“简单”算法,将会使得训练的效益放大!

三个层面和四种定式

下面我们将会从三个层面来思考,编写多种样式的二分查找,编写的过程中我会去尝试考虑到所有边边角角的情况

哪三个层面呢?

可以写成哪几个定式呢?

模板题目

题目:已知一个从小到大的整型数组 arr,我们想找到目标值 target 在数组中的下标值,找到返回下标,找不到返回 -1

定式一

模板

public int function(int[] arr, target) {
    int left = 0, right = arr.length - 1;
    while (left <= right) {
        int mid = left + (right - left >> 1);
        if (target > arr[mid]) {
            left = mid + 1;
        }
        else if (target < arr[mid]) {
            right = mid - 1;
        }
        else {
            return mid;
        }
    }
    return -1;
}

结论

思考

如何记忆

其实这种写法是本人最常用的二分查找的写法,写法如何记忆呢?结论又如何理解记忆呢?

定式二

模板一

public int function(int[] arr, int target) {
    int left = 0. right = arr.length - 1;
    while (left <= right) {
        int mid = left + (right - left >> 1);
        if (target >= arr[mid]) {
            left = mid + 1;
        }
        else {
            right = mid - 1;
        }
    }
    return right >= 0 ? (target == arr[right] ? right : -1) : -1;
}

结论

模板二

public int function(int[] arr, int target) {
    int left = 0. right = arr.length - 1;
    while (left <= right) {
        int mid = left + (right - left >> 1);
        if (target <= arr[mid]) {
            right = mid - 1
        }
        else {
            left = mid + 1;;
        }
    }
    return left < arr.length ? (target == arr[left] ? left : -1) : -1;
}

结论

思考

如何记忆

这种写法其实也蛮常用的,这种定式也是必掌握的

定式三

模板

public int function(int[] arr, int target) {
    int left = 0, right = arr.length - 1;
    while (left < right - 1) {
        int mid = left + (right - left >> 1);
        if (target > arr[mid]) {
            left = mid;
        }
        else if (target < arr[mid]) {
            right = mid;
        }
        else {
            return mid;
        }
    }
    if (target == arr[left]) {
        return left;
    }
    else if (target == arr[right]) {
        return right;
    }
    return -1;
}

结论

思考

其实这种写法很麻烦,一般不会写成left = midright = mid,因为非要这样写会将许多特殊的边边角角情况写进去,比较繁杂。我们来稍微分析一下

如何记忆

这种写法还是平常少些,当然理解和思维过程还是走一遍为好,加深记忆,同时方便在写二分查找题目时候,遇到各种题型能够快速在脑中间建立各种各样的模型

定式四

模板

public int function(int[] arr, int target) {
    int left = 0, right = arr.length;
    while (left < right) {
        if (target > arr[mid]) {
            left = mid + 1;
        }
        else if (target < arr[mid]) {
            right = mid;
        }
        else {
            return mid;
        }
    }
    return -1;
}

结论

思考

如何记忆

其实这个定式我用的也不多

总结

本人一般常用定式一和定式二,一般题目都是在定式一的基础上修改一下,定式二对于一些题目还是挺方便的,定式三基本没用到过。其实定式二,定式三,定式四都是在定式一的情况下的拓展。

 

标签:arr,right,吃透,target,mid,目标值,奇技淫巧,leetcode,left
来源: https://blog.51cto.com/u_13281972/2997911