《五月集训》第九日——二分查找

前言

这是五月集训的第9日,今日的训练内容是 二分查找

二分查找的概述

二分查找顾名思义,就是使用二分法的方式来寻找一个 有序 数组中的某一个 target 的方法。这样就成功的把时间复杂度从 n 降低到了 logn 。它的写法可以参考如下:

给一个有序数组和一个值target,查找有无这个值,如果没有就返回 -1。

int search(int* nums, int numsSize, int target){
    int l=0,r=numsSize-1;           //(1)
    while(l<=r){                    //(2)
        int mid = (l+r)/2;          //(3)
        if(nums[mid]>target){       //(4)
            r = mid - 1;
        }
        if(nums[mid]<target){       //(5)
            l = mid + 1;
        }
        if(nums[mid]==target){      //(6)
            return mid;
        }
    }
    return -1;                      //(7)
}

以这题举例来大致对二分查找进行概述:
1.首先定义一下区间的左端点和右端点,并且初始化值为 0numsSize-1
2.最多循环到两边的端点相重合,说明整个数组已经被遍历完全了。
3.定义一个区间中点(这个中点指的是左端点到右端点的那个区间)
4.如果区间中点的值是大于目标值的,那么说明目标值在区间的左边,那么可以缩小区间的范围了,将右端点移动到刚刚区间中点的左边开始遍历。
5.类似上面。
6.如果区间中点就等于目标值,那说明找到要的值了,直接返回区间中点下标就可以了。
7.如果遍历完了数组都不存在的话,说明没有这个值,返回 -1。

解题报告

1.力扣35

原题链接

https://leetcode.cn/problems/search-insert-position/

题目概述

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

请必须使用时间复杂度为 O(log n) 的算法。

解题思路

所给的数组是一个有序的数组,因此这个题目是一个二分查找的题目。在仔细读题并且梳理过后可以得出,本题其实需要找的就是大于等于 target 的最小下标,因为最后需要顺序插入的数字肯定是小于后一个数的,并且插入后其实占用的就是其后一个数在插入前的位置,因此只要找到大于等于 target 的最小下标即可。然后使用二分法不断缩小范围即可。

源码剖析

int searchInsert(int* nums, int numsSize, int target){
    int l=0,r=numsSize-1;
    int ans=numsSize;
    while(l<=r){
        int mid = (l+r)/2;
        if(nums[mid]>target){
            ans = mid;
            r = mid-1;
        }
        if(nums[mid]<target) l = mid+1;
        if(nums[mid]==target) return mid;
    }
    return ans;
}

定义了一个用于存储满足题目要求数据的变量 ans ans时刻都是大于 target 的最小下标。

2.力扣704

原题链接

https://leetcode.cn/problems/binary-search/

题目概述

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

解题思路

这题就是前面介绍二分查找使用的例题,详见介绍。

源码剖析

int search(int* nums, int numsSize, int target){
    int l=0,r=numsSize-1;           //(1)
    while(l<=r){                    //(2)
        int mid = (l+r)/2;          //(3)
        if(nums[mid]>target){       //(4)
            r = mid - 1;
        }
        if(nums[mid]<target){       //(5)
            l = mid + 1;
        }
        if(nums[mid]==target){      //(6)
            return mid;
        }
    }
    return -1;                      //(7)
}

3.力扣剑指offer 53

原题链接

https://leetcode.cn/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/

题目概述

统计一个数字在排序数组中出现的次数。

解题思路

首先还是对原来的数组进行二分查找,如果没有找到这个数的话则说明原来的数组中本来就没有这个数,那么直接返回0就可以了。如果找到了这个数,因为这个数组是有序的数组,所以相同的数字的位置肯定是相邻的,那么也就只需要把之前找到的下标分别向前向后遍历一下,一旦发现了不相等的数字直接 break; 就可以了,每找到一个数就给计数器加一(找到数字的当时就要使得计数器加一),最后返回累加的值就可以了。

源码剖析

int search(int* nums, int numsSize, int target){
    if(numsSize==0) return 0;
    int mid1 = 0;
    int ans = 0;
    int l = 0, r = numsSize-1;
    while(l<=r){
        int mid = (l+r)/2;
        if(nums[mid]>target) r = mid-1;
        if(nums[mid]<target) l = mid+1;
        if(nums[mid]==target){
            ans++;
            mid1 = mid;
            break;
        }
    }
    if(nums[mid1]==target){
        int x = mid1;
        for(x=mid1-1;x>=0;--x){
            if(nums[x]==target) ans++;
            else break;
        }
        for(x=mid1+1;x<numsSize;++x){
            if(nums[x]==target) ans++;
            else break;
        }
    }
    return ans;
}

值得注意的是,当数组的容量为0时,直接使用判断语句处理会比较方便,不需要考虑下标等各种问题。

4.力扣911

原题链接

https://leetcode.cn/problems/online-election/

题目概述

给你两个整数数组 persons 和 times 。在选举中,第 i 张票是在时刻为 times[i] 时投给候选人 persons[i] 的。

对于发生在时刻 t 的每个查询,需要找出在 t 时刻在选举中领先的候选人的编号。

在 t 时刻投出的选票也将被计入我们的查询之中。在平局的情况下,最近获得投票的候选人将会获胜。

实现 TopVotedCandidate 类:

TopVotedCandidate(int[] persons, int[] times) 使用 persons 和 times 数组初始化对象。
int q(int t) 根据前面描述的规则,返回在时刻 t 在选举中领先的候选人的编号。

解题思路

暂时做记录,等学习 c++ 后回来重新思考。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值