牛客面试必刷TOP101——二分查找排序

2023-11-10

[二分查找-I BM17]

原题

请实现无重复数字的升序数组的二分查找。给定一个 元素升序的、无重复数字的整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标(下标从 0 开始),否则返回 -1

class Solution:
    def search(self , nums: List[int], target: int) -> int:
        if not nums:
            return -1
        l = 0
        r = len(nums)-1
        while l <= r:
            m = l+(r-l)//2
            if target < nums[m]:
                r = m-1
            elif target > nums[m]:
                l = m+1
            else:
                return m
        return -1
[二维数组中的查找 BM18]

原题

在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]]
给定 target = 7,返回 true。给定 target = 3,返回 false。

思路:左到右递增、上到下递增

从右上角元素开始,左边的元素都比其小,下面的元素都比其大,因此可以通过判断target和该元素的值确定左移还是下移。

class Solution:
    def Find(self , target: int, array: List[List[int]]) -> bool:
        # write code here
        if len(array) == 0:
            return False
        i = 0
        j = len(array[0])-1
        while i< len(array) and j >= 0:
            if target < array[i][j]:
                j -= 1
            elif target > array[i][j]:
                i += 1
            else:
                return True
        return False
[寻找峰值 BM19]

原题

给定一个长度为n的数组nums,请你找到峰值并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个所在位置即可。

1.峰值元素是指其值严格大于左右相邻值的元素。严格大于即不能有等于

2.假设 nums[-1] = nums[n] = −∞

3.对于所有有效的 i 都有 nums[i] != nums[i + 1]

4.你可以使用O(logN)的时间复杂度实现此问题吗?

思路1

题目保证没有相同元素,峰值是左右元素严格小于本身,所以最大值一定是峰值,因此只需要找到最大值的位置即可。

class Solution:
    def findPeakElement(self , nums: List[int]) -> int:
        # write code here
        return nums.index(max(nums))

思路2:分治法

假设数组两边是负无穷,则只要从两边往高处走,即可找到峰值。

  • 取中间元素mid,按照二分法可以将数组划分为【l , mid】和【mid+1 , r】
  • 比较nums[mid]和nums[mid+1]的值
  • 如果左边值大,说明右区间在下降,显然向右走不符合“往高处走",因此将区间往左缩小,即 r = m i d r=mid r=mid
  • 如果右边值大,说明向右走是在往高处走,因此将区间往右缩小, l = m i d + 1 l=mid+1 l=mid+1
  • l = = r l==r l==r时即到达峰值
class Solution:
    def findPeakElement(self , nums: List[int]) -> int:
        # write code here
        l = 0
        r = len(nums)-1
        while l < r:
            mid = (l+r) // 2
            if nums[mid] <= nums[mid+1]:
                l = mid + 1
            else:
                r = mid
        return l
[组中的逆序对 BM20 ]

原题

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007

思路

归并排序过程中计算逆序对,对于a和b,逆序对个数=a逆序对+b逆序对+合并ab的逆序对

  • 划分数组,直至子数组长度为1
  • 归并排序合并子数组,并计算逆序对
  • i指向相邻子数组左数组,j指向相邻子数组右数组;
  • 左到右遍历合并,对于 d a t a ( l , m i d , r ) data(l,mid,r) data(l,mid,r),令i = l, j = mid+1,依次将最小的元素填入合并的排序数组中。此处需要辅助数组temp存储原始两个待合并数组元素, d a t a ( l : r + 1 ) data(l:r+1) data(l:r+1)用于存储合并后数组元素。k的范围为(l,r),如果 t e m p [ i ] < = t e m p [ j ] temp[i]<=temp[j] temp[i]<=temp[j],则将 t e m p [ i ] temp[i] temp[i]填入data[k]中,如果 t e m p [ i ] > t e m p [ j ] temp[i]>temp[j] temp[i]>temp[j]大,说明存在逆序对,需要计算个数。考虑到temp(i : mid)本身就是有序的,所以如果temp[i]大于temp[j],那么 t e m p ( i + 1 , m i d ) temp(i+1,mid) temp(i+1,mid)都大于 t e m p [ j ] temp[j] temp[j],所以对于 t e m p [ j ] temp[j] temp[j]而言,前面可以有 m i d − i + 1 mid-i+1 midi+1个元素和其构成逆序对。因此当出现 t e m p [ i ] > t e m p [ j ] temp[i] > temp[j] temp[i]>temp[j]时,逆序对增加 m i d − i + 1 mid-i+1 midi+1个。
  • 还有一种方式是从右到左遍历,即每次将最大的元素填入合并的排序数组中,过程和左到右遍历类似。
class Solution:
    def InversePairs(self , data: List[int]) -> int:
        # write code here
        def divide(data,l,r,temp):
            if l >= r:
                return 0
            mid = (l+r)//2
            r1 = divide(data,l,mid,temp)
            r2 = divide(data,mid+1,r,temp)
            res = (r1 + r2) % 1000000007
            i,j = l,mid+1
            for k in range(l,r+1):
                temp[k] = data[k]
            for k in range(l,r+1):
                if i == mid+1:
                    data[k] = temp[j]
                    j += 1
                elif j == r+1 or temp[i] <= temp[j]:
                    data[k] = temp[i]
                    i += 1
                else:
                    res += mid - i + 1
                    data[k] = temp[j]
                    j += 1
            return res % 1000000007
             
        temp = [0 for _ in range(len(data))]
        return divide(data, 0, len(data)-1,temp)

#从右到左遍历
class Solution:
    def InversePairs(self , data: List[int]) -> int:
        # write code here
        def divide(data,l,r,temp):
            if l >= r:
                return 0
            mid = (l+r)//2
            r1 = divide(data,l,mid,temp)
            r2 = divide(data,mid+1,r,temp)
            res = (r1 + r2) % 1000000007
            i,j = mid,r
            for k in range(l,r+1):
                temp[k] = data[k]
            for k in range(r,l-1,-1):
                if j == mid:
                    data[k] = temp[i]
                    i -= 1
                elif i == l-1 or temp[i] <= temp[j]:
                    data[k] = temp[j]
                    j -= 1
                else:
                    res += j - mid
                    data[k] = temp[i]
                    i -= 1
            return res % 1000000007
             
        temp = [0 for _ in range(len(data))]
        return divide(data, 0, len(data)-1,temp)
[旋转数组的最小数字 BM21]

原题

有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

思路1

暴力法,原始非递减,旋转一部分元素放在数组后面,得到的旋转数组其实是两段非递减元素,因此只需要找到转折点,即后一个元素小于前面元素的位置,即为原来数组的第一个元素,也即最小元素。如果没有这个转折点,说明旋转数组的第一个元素就是最小的元素。

class Solution:
    def minNumberInRotateArray(self , rotateArray: List[int]) -> int:
        # write code here
        for i in range(1,len(rotateArray)):
            if rotateArray[i] < rotateArray[i-1]:
                return rotateArray[i]
        return rotateArray[0]

思路2

利用数组有序,可以使用二分法

  • 初始化左右边界l,r
  • 取中间值mid【对于数组[4,5,6,7,1,2],[4,5,6,7]为左排序数组,[1,2]为右排序数组】
  • 如果中间值小于最右边的值,说明中间值一定在右排序数组中;此时就缩小区间,r = mid
  • 如果中间值大于最右边的值,说明中间值一定在左排序区间中;此时缩小左区间,l = mid + 1
class Solution:
    def minNumberInRotateArray(self , rotateArray: List[int]) -> int:
        # write code here
        l,r = 0,len(rotateArray)-1
        while l < r:
            mid = (l+r) // 2
            if rotateArray[mid] < rotateArray[r]:
                r = mid
            elif rotateArray[mid] > rotateArray[r]:
                l = mid + 1
            else:
                r -= 1
        return rotateArray[l]
[比较版本号 BM22]

原题

牛客项目发布项目版本时会有版本号,比如1.02.11,2.14.4等等。现在给你2个版本号version1和version2,请你比较他们的大小。版本号是由修订号组成,修订号与修订号之间由一个"."连接。1个修订号可能有多位数字组成,修订号可能包含前导0,且是合法的。例如,1.02.11,0.1,0.2都是合法的版本号;每个版本号至少包含1个修订号。修订号从左到右编号,下标从0开始,最左边的修订号下标为0,下一个修订号下标为1,以此类推。

比较规则:
一. 比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较忽略任何前导零后的整数值。比如"0.1"和"0.01"的版本号是相等的
二. 如果版本号没有指定某个下标处的修订号,则该修订号视为0。例如,“1.1"的版本号小于"1.1.1”。因为"1.1"的版本号相当于"1.1.0",第3位修订号的下标为0,小于1
三. version1 > version2 返回1,如果 version1 < version2 返回-1,不然返回0.

思路

使用**“.”**分割符将字符串划分,并将其转化为整数数组,就无需处理前导零,依次比较每个位置的元素
预处理:1、将两个数组长度对齐,短数组后部补0;2、记录最短长度遍历,遍历完再遍历另一个数组多出的元素,此处采用第一种预处理

class Solution:
    def compare(self , version1: str, version2: str) -> int:
        # write code here
        v1 = list(map(int,version1.split('.')))
        v2 = list(map(int,version2.split('.')))
        if len(v1) < len(v2):
            for j in range(len(v1),len(v2)):
                v1.append(0)
        elif len(v1) > len(v2):
            for j in range(len(v2),len(v1)):
                v2.append(0)
        for i in range(len(v1)):
            if v1[i] < v2[i]:
                return -1
            elif v1[i] > v2[i]:
                return 1
        return 0
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

牛客面试必刷TOP101——二分查找排序 的相关文章

  • Oracle case when 详解

    文章目录 1 概述 2 示例 when 执行顺序 3 ORA 06592 执行 CASE 语句时未找到 CASE 1 概述 1 case when 条件判断语句 1 相当于其它语言中的 if else 2 部分情况下 等同于 decode

随机推荐

  • Swagger & Knife4j

    Swagger Knife4j 1 Swagger介绍 1 简介 Swagger 是一个规范和完整的框架 用于生成 描述 调用和可视化 RESTful 风格的 Web 服务 https swagger io 它的主要作用是 使得前后端分离开
  • vue+element 实现表格,键盘上下键选择某一行,并选中

    1 直接上代码
  • Maven安装步骤汇总

    Maven安装步骤汇总 最近老是换机器开发 机器上又没有Maven 每次都要下载 安装 重复写配置文件 很麻烦 而习惯的常用配置网上教程很分散 故做整合 目录 Maven下载安装 配置环境变量测试 修改maven配置文件 3 1 修改本地仓
  • STM32在应用编程(IAP)详解

    什么是IAP STM32单片机的程序烧写有多种方法 分别为 JTAG SW ISP IAP gt JTAG的方式需要专用的烧写工具 在产品布置到现场后 更新产品程序比较麻烦 gt ISP即为 在系统编程 In System Programm
  • python之计算系统空闲内存、列表字典相互转换

    python之计算系统空闲内存 usr bin env python coding utf8 Time 2017 11 30 14 25 Author hantong File count free memory py 统计linux系统空
  • element date-picker range类型时间选择器 限制选中前后7天的时间的方法

    实现效果 代码
  • 使用webpack-bundle-analyzer分析uni-app 的微信小程序包大小(HbuilderX运行)

    1 找到vue config js 文件 如果找不到 则在项目根目录下 跟pages json同一个目录下 创建一个JS文件 命名为vue config js 2 安装webpack bundle analyzer 官方网站 https g
  • Java连接数据库警告WARN: Establishing SSL connection without server's identity ......

    今天搭了个框架 发现数据库发出了警告 Fri Mar 23 13 49 33 CST 2018 WARN Establishing SSL connection without server s identity verification
  • python乱码怎么办_解决python发送邮件乱码问题

    使用python发邮件很简单 但是遇到乱码问题很烦恼 乱码问题有几种 有发件人名称乱码 有标题乱码 也有正文乱码的问题 一 发件人名称乱码 要解决发件人名称乱码问题 必须使用Header 如下代码 from email header imp
  • 记忆化搜索简介

    记忆化搜索 算法上依然是搜索的流程 但是搜索到的一些解用动态规划的那种思想和模式作一些保存 一般说来 动态规划总要遍历所有的状态 而搜索可以排除一些无效状态 更重要的是搜索还可以剪枝 可能剪去大量不必要的状态 因此在空间开销上往往比动态规划
  • 【IDEA】Idea 报错 Module was compiled with an incompatible version of Kotlin. The binary version of its

    1 场景1 提示 在项目本地DEBUG或者build的时候报了以下错误 kotlin stdlib common kotlin module Module was compiled with an incompatible version
  • vant的中文的文档

    vant的中文的文档 拿走把小傻瓜 https vant contrib gitee io vant zh CN 但是本着点赞自愿 收藏吃灰 还是多少可以支持一下
  • TCP连接的状态详解以及故障排查

    1 TCP状态 了解TCP之前 先了解几个命令 linux查看tcp的状态命令 1 netstat nat 查看TCP各个状态的数量 2 lsof i port 可以检测到打开套接字的状况 3 sar n SOCK 查看tcp创建的连接数
  • C++中非常好用的泛型函数

    1 泛型函数 泛型函数结合lambda函数可以实现很多功能如 将序列中的每个负数替换为其绝对值 transform vi begin vi end vi begin int x return x lt 0 x x 查找第一个长度大于等于sz
  • Camera ISP

    1 ISP工作原理 ISP Image Signal Processor 即图像信号处理 主要作用是对前端图像传感器输出的信号做后期处理 依赖于 ISP 才能在不同的光学条件下都能较好的还原现场细节 景物通过 Lens 生成的光学图像投射到
  • 微信小程序- 选择器 合并时间和日期

    https developers weixin qq com miniprogram dev component picker html 从底部弹起的滚动选择器 类型有普通选择器 多列选择器 时间选择器 日期选择器 省市区选择器 没有现成的
  • Unity3D打包时,编译错误

    最近打包Unity3D的APK包时 报编译错误 Failed to compile Java code to DEX D Work Parkour branches 20131219 box360 Temp StagingArea gt j
  • VLC裁剪和移植到S3C6410

    1 由于项目需求 这几天在折腾VLC 需要将它裁剪并移植到PowerPC上 由于板子没有到 先在6410上跑 目前从需求看我们只要VLC作为一个server即可 先贴配置 如下 几乎disable掉所有东东 bin sh for arm C
  • Loki搭建日志收集系统

    Loki 什么是Loki Loki是受Prometheus启发的水平可扩展 高度可用的多租户日志聚合系统 他被设计为非常经济高效且易于操作 它不索引日志内容 而是为每个日志流设置一组标签 Loki文档网址 https grafana com
  • 牛客面试必刷TOP101——二分查找排序

    列表 二分查找 I BM17 二维数组中的查找 BM18 寻找峰值 BM19 组中的逆序对 BM20 旋转数组的最小数字 BM21 比较版本号 BM22 二分查找 I BM17 原题 请实现无重复数字的升序数组的二分查找 给定一个 元素升序