二分法查找及有重复值的二分法

前言

最近也在进行一些面试嘛,也见识到了很多各种各样的题目,其中就有一些和二分查找相关的.

二分查找,在有序的数组中快速找到目标值.

这个算法在上学的时候学过,之后就没有看过了,因为比较"简单"嘛~.

然而在面试过程中,我在二分查找及类似题目上栽了三次…

所以今天做一个总结.

注意:下文的代码中没有进行参数校验,实际使用时需要进行参数校验

普通写一个二分查找

class Solution:
    def binary_search(self,arr,target):
        low = 0
        high = len(arr) - 1
        while low <= high:
            mid = (low + high) // 2
            if arr[mid] == target:
                return mid
            elif arr[mid] > target:
                high = mid - 1
            else:
                low = mid + 1
        return


if __name__ == "__main__":
    c = Solution()
    print(c.binary_search([1,2,3,4,5,6],4))

数组中有重复值的问题

我大部分都载在这里了,

当数组中有重复值的时候,返回该值第一个出现或者最后一个出现的下标.

比如[1,2,2,2,2,2,3],如果返回第一个出现的地方,应该返回1,如果返回最后一个,应该返回5.

首先我们可以想到,可以通过二分法拿到某一个目标值,然后向左或者向右遍历找到边界.

这个算法的时间复杂度是O(n),在最坏情况下,即整个数组的值相同时,算法时间消耗为n/2,还是O(n)是不太令人满意的.

那么有没有更好的办法呢?有的,还是二分法,二分法在找到目标值时,进行了返回,我们可以不让其返回,继续查找下一个即可.

代码实现如下:

查找最左目标值

    def binary_search2(self ,arr , target):
        low = 0
        high = len(arr) - 1
        while low < high:
            mid = (low + high) // 2
            if arr[mid] >= target:
                high = mid
            else:
                low = mid + 1
        if arr[high] == target:
            return high
        return -1 

在这个实现中,当找到某个目标值时,按照原二分法中的大于处理,因为我们想找到的是最左目标值.

注意,这个方法和上面的二分法有几处差异.

首先当找到的值大于等于目标值时,high=mid,因为此时的值可能就是最左目标值,因此需要将当前值继续保留在下一次查找的区间内.

而当小于的时候,那么肯定不是最终目标,采用low = mid + 1来处理,同时这一步可以保证不会出现死循环.(如果low= mid,在[0,1]中查找1的时候会陷入死循环.low=0,high=1,mid=0.arr[mid]<target,low=mid=0,经过一次循环,low和high无变化,陷入死循环.).

稍加修改可以实现寻找最右的值.

    def binary_search3(self ,arr , target):
        low = 0
        high = len(arr) - 1
        while low < high:
            mid = (low + high) // 2 + 1
            if arr[mid] <= target:
                low = mid
            else:
                high = mid - 1
        if arr[high] == target:
            return high
        return -1   

注意在第五行,mid = ( low + high )// 2 + 1这里和上面不一样,添加了+1,因为当在[0,1]寻找0时会陷入死循环.

合并一下,拿到一个方法.

上面两个其实实现的只是最左/最右的区别,因此可以合并一下,成为一个方法.

    # flag=0,获取最左目标,否则获取最后目标
    def binary_search4(self ,arr , target, flag):
        low = 0
        high = len(arr) - 1
        while low < high:
            mid = (low + high) // 2
            if arr[mid] == target:
                if flag == 0:
                    high = mid
                else :
                    low = mid
            elif arr[mid] < target:
                low = mid + 1
            else:
                high = mid - 1
        if arr[high] == target:
            return high
        return -1

其实我们可以发现,当想要获取最左最右的时候,区别对待的只是当前值=目标值这一个逻辑,因此我们将相等的逻辑单独写,并且根据flag的值进行不同的处理(分别任high或者low等于mid).而小于和大于的逻辑都是不变的,进行减一或者加一操作,将当前值从待查区间去掉.

总结

二叉查找大家都清楚,那么变种查最左或者最右,其实只需要将相等的逻辑考虑为大于/小于即可,之后在循环结束后比较当前的index的值是否是目标值就可.剩下的就是代码中一些小情况,尤其是死循环的处理也要注意一下.

其实不要觉得二分法查找太简单没有用,我原来也是这样想的…后来看了一些文章,才知道二叉树的应该之广泛,比如下面参考链接中的文章.

我遇到的面试题目呢,主要有两个,一个就是很直接让你查找目标值的最左或者最右,另一个就是需要你查出目标值的区间起始下标.

这个时候如果你能写出上面这个方法,调用两次即可返回结果,岂不是美滋滋.

参考链接

http://hedengcheng.com/?p=595

完。




ChangeLog
2019-03-17 完成

以上皆为个人所思所得,如有错误欢迎评论区指正。

欢迎转载,烦请署名并保留原文链接。

联系邮箱:huyanshi2580@gmail.com

更多学习笔记见个人博客------>呼延十

  • 13
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
二分法查找重复元素的C语言实现可以通过以下步骤来完成: 1. 首先,要确保待查找的数组是已经排序好的。如果数组未排序,可以先进行排序操作,例如使用快速排序或归并排序等算法。 2. 定义一个二分查找函数,接受待查找的数组数组长度和目标元素作为参数。 3. 在二分查找函数中,使用两个变量 `low` 和 `high` 分别表示当前查找范围的起始位置和结束位置。 4. 在每一次循环中,计算中间位置 `mid`,即 `mid = (low + high) / 2`。 5. 检查数组中 `mid` 位置的元素是否等于目标元素。如果相等,则说明找到了一个重复元素,返回 `mid`。 6. 如果目标元素小于 `mid` 位置的元素,则说明目标元素可能在左半部分,将 `high` 更新为 `mid - 1`。 7. 如果目标元素大于 `mid` 位置的元素,则说明目标元素可能在右半部分,将 `low` 更新为 `mid + 1`。 8. 重复步骤 4 到步骤 7,直到找到重复元素或者 `low` 大于 `high`。 下面是一个简单的示例代码: ```c #include <stdio.h> int binarySearch(int arr[], int n, int target) { int low = 0; int high = n - 1; while (low <= high) { int mid = (low + high) / 2; if (arr[mid] == target) { return mid; } else if (arr[mid] > target) { high = mid - 1; } else { low = mid + 1; } } return -1; // 如果未找到重复元素,返回 -1 } int main() { int arr[] = {1, 2, 3, 4, 4, 5, 6}; int n = sizeof(arr) / sizeof(arr[0]); int target = 4; int index = binarySearch(arr, n, target); if (index != -1) { printf("重复元素 %d 的位置为 %d\n", target, index); } else { printf("未找到重复元素 %d\n", target); } return 0; } ``` 在上述代码中,我们定义了一个 `binarySearch` 函数来执行二分查找操作。在 `main` 函数中,我们定义了一个已排序的数组 `arr`,然后调用 `binarySearch` 函数来查找重复元素。如果找到了重复元素,将打印出其位置;否则,将打印出未找到的提示信息。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值