蓝桥杯算法竞赛系列第四章——二分算法

news2024/11/24 17:15:48

欢迎回到:遇见蓝桥遇见你,不负代码不负卿! 

目录

引入:二分查找

题目描述

 题解

代码执行

复杂度分析

例题一:搜索插入位置

题目描述

题解

代码执行

复杂度分析

例题二:寻找峰值

题目描述

题解

代码执行

 复杂度分析

例题三:搜索二维矩阵

题目描述

 题解

 代码执行

思考题

最大子序和

题目描述

代码执行

蓝桥结语:遇见蓝桥遇见你,不负代码不负卿!


好久不见啦铁汁们,蓝桥杯更新咯,快来尝尝鲜叭。

【前言】:由于本章基础知识点不多,所以笔者直接讲解四道典型题让大家感受一下二分法的美妙。

准备开始咯,坐稳哈...

引入:二分查找

【敲黑板】:用二分算法解题的前提是该数组有序!!!

【注意】:查找一次砍掉一半,效率非常高!但是条件比较苛刻,一定要有序! 

题目描述

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

示例1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

 示例2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
 

 题解

设定左右指针
找出中间位置,并判断该位置值是否等于 target
nums[mid] == target 则返回该位置下标
nums[mid] > target 则右侧指针移到中间
nums[mid] < target 则左侧指针移到中间

二分是一个比较简单的算法,只要大家记住上面这种套路就行啦。

代码执行

int search(int* nums, int numsSize, int target){
    //考虑特殊情况
    if(nums == NULL || numsSize == 0){
        return -1;
    }
    int left = 0;//起始元素的索引
    int right = numsSize - 1;//末尾元素的索引
    int mid = 0;
    while(left <= right){
        //应该有很多人会写成mid = (left + right) / 2;这种写法不谨慎
        //因为做的是加法运算,所以要考虑溢出的特殊情况
        mid = left + (right - left) / 2;
        if(nums[mid] < target){
            left = mid + 1;
        }else if(nums[mid] > target){
            right = mid - 1;
        }else{
            return mid;
        }
    }
    return -1;
}

【注意】:while判断表达式中是left <= right, 为什么还要加上left == right 的情况呢,当二者相等的时候说明还有一个元素需要被比较,所以当left > right时停下来,因为此时中间已经没有元素需要被比较了。至于为什么将mid = left + (right - left) / 2; 的形式,上面代码中已经讲咯。

复杂度分析

时间复杂度:O(logN)

空间复杂度:O(1)

看,二分法是很高效的,大家在今后的训练中,如果遇到查找搜索类的题目要求时间复杂度是O(logN)的,要想到二分法哦。 

好嘞,这就是二分查找,是不是很简单,下面再补充几道典型例题,让大家熟悉二分。

例题一:搜索插入位置

题目描述

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

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

题目中要求使用时间复杂度为O(logN)的算法解题,所以结合本题题意,很容易就想到了二分法。

示例1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例2:

输入: nums = [1,3,5,6], target = 7
输出: 4

题解

  • 整体思路和普通的二分查找几乎没有区别,先设定左侧下标 left 和右侧下标 right,再计算中间下标 mid
  • 每次根据 nums[mid] 和 target 之间的大小进行判断,相等则直接返回下标,nums[mid] < target 则 left 右移,nums[mid] > target 则 right 左移
  • 查找结束如果没有相等值则返回 left,该值为插入位置,注意哦,最后如果没有相等值,返回的是left

【注意】:

二分查找的思路不难理解,但是边界条件容易出错,比如 循环结束条件中 left 和 right 的关系,更新 left 和 right 位置时要不要加 1 减 1。这些都是大家自己动手画图理解,用代码去体会,只可意会不可言传哦。

代码执行

int searchInsert(int* nums, int numsSize, int target){
    //考虑特殊情况
    if(nums == NULL || numsSize == 0){
        return -1;
    }
    int left = 0;
    int right = numsSize - 1;
    int mid = 0;
    while(left <= right){
        mid = left + (right - left) / 2;
        if(nums[mid] > target){
            right = mid - 1;
        }else if(nums[mid] < target){
            left = mid + 1;
        }else{
            return mid;
        }
    }
    return left;
}

复杂度分析

时间复杂度:O(logN)

空间复杂度:O(1)

是不是很简单,下面开始蹭加点难度咯,有点绕,需要仔细想哦,加油加油。

 

例题二:寻找峰值

题目描述

峰值元素是指其值严格大于左右相邻值的元素。

给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞ 。

你必须实现时间复杂度为 O(log n) 的算法来解决此问题。

示例1:

输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。

示例2:

输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5 
解释:你的函数可以返回索引 1,其峰值元素为 2;
     或者返回索引 5, 其峰值元素为 6。

题解

【注意】:二分法解题的前提要求是有序的,这题看起来没说有序呀,那怎么还能用二分法呢,我们也不能先进行排序,因为会把索引打乱,那该怎么办呢,请朝后看...

描述这个规律就是:

  • 规律一:如果nums[mid] > nums[mid+1],则在mid之前一定存在峰值元素
  • 规律二:如果nums[mid] < nums[mid+1],则在mid+1之后一定存在峰值元素 

代码执行

int findPeakElement(int* nums, int numsSize){
    //考虑特殊情况
    if(nums == NULL || numsSize == 0){
        return -1;
    }
    int left = 0;
    int right = numsSize - 1;
    int mid = 0;
    while(left <= right){
        if(left == right){
            return left;
        }
        mid = left + (right - left) / 2;
        if(nums[mid] > nums[mid + 1]){
            right = mid;//mid之前一定存在峰值元素
        }else{
            left = mid + 1;//mid+1之后一定存在峰值元素
        }
    }
    return left;
}

 复杂度分析

时间复杂度:O(logN)

空间复杂度:O(1)

例题三:搜索二维矩阵

题目描述

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

每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。

也就是说,整个二维数组都是升序的。

示例1:

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true

 示例2:

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false

 题解

【注意】:本题的重点就在于一维索引和二维索引间的互换 

 代码执行

bool searchMatrix(int** matrix, int matrixSize, int* matrixColSize, int target){
    //考虑特殊情况
    if(matrix == NULL || matrixSize == 0){
        return false;
    }
    int row = matrixSize;//行数
    int col = *matrixColSize;//列数
    int left = 0;//起始元素的索引
    int right = row * col - 1;//最后一个元素的索引
    int mid = 0;
    int element = 0;
    while(left <= right){
        mid = left + (right - left) / 2;
        element = matrix[mid / col][mid % col];//将一维的索引化成二维的索引
        if(element == target){
            return true;
        }else if(element > target){
            right = mid - 1;
        }else{
            left = mid + 1;
        }
    }
    return false;
}

复杂度分析

时间复杂度:O(logM*N), M,N分别是矩阵的行数和列数

空间复杂度:O(1) 

好喽,铁汁把上面四道题目练习一遍,肯定就能对二分算法有一定的认识,加油加油哦。

思考题

大家先将上篇博文(分治算法)看一下,现在讲解那道留下的思考题。

蓝桥杯算法竞赛系列第三章——细谈递归的bro分治_安然无虞的博客-CSDN博客

最大子序和

题目描述

给定一个整数数组,找到一个具有最大和的连续子数组(子数组中至少包含一个数),要求返回其最大和。

注意:本题要求的是连续的子数组,可能有的题目要求可以是断开的,但本题不是。

将数组nums由中点mid分为三种情况:

1. 最大子串在左边

2. 最大子串在右边

3. 最大子串跨中点,左右两边元素都有(理解上的难点)

 

lSum 表示 [l,r] 内以 l 为左端点的最大子段和
rSum 表示 [l,r] 内以 r 为右端点的最大子段和
mSum 表示 [l,r] 内的最大子段和
iSum 表示 [l,r] 的区间和

代码执行

struct Status {
    int lSum, rSum, mSum, iSum;
};

struct Status pushUp(struct Status l, struct Status r) {
    int iSum = l.iSum + r.iSum;
    int lSum = fmax(l.lSum, l.iSum + r.lSum);
    int rSum = fmax(r.rSum, r.iSum + l.rSum);
    int mSum = fmax(fmax(l.mSum, r.mSum), l.rSum + r.lSum);
    return (struct Status){lSum, rSum, mSum, iSum};
};

struct Status get(int* a, int l, int r) {
    if (l == r) {
        return (struct Status){a[l], a[l], a[l], a[l]};
    }
    int m = l + (r - l) / 2;
    struct Status lSub = get(a, l, m);
    struct Status rSub = get(a, m + 1, r);
    return pushUp(lSub, rSub);
}

int maxSubArray(int* nums, int numsSize) {
    return get(nums, 0, numsSize - 1).mSum;
}

由于想让大家搞懂知识点,所以这里的题目可能用这种解法不是最优解,没关系,我会在后面的算法中补充到,在每日一题中也会涉及到。今天就不布置思考题咯,铁汁们好好把上面题目自己尝试做出来。

蓝桥结语:遇见蓝桥遇见你,不负代码不负卿!

嘿嘿,期待铁汁们留言点评,如果能够再动动小手,给博主来个三连那就更好啦,您的认可就是我最大的动力!求求啦~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/478740.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【五一创作】python 基础系列篇:八、熟练掌握推导式

python 基础系列篇&#xff1a;八、熟练掌握推导式 推导式特殊的元组推导式 推导式机制玩转推导式小结 推导式 在python提供的各种语法糖中&#xff0c;老顾最青睐的就是这个推导式&#xff0c;他大大减少了代码的书写量。 比如一个正常的&#xff0c;生成长度为5的列表&…

红黑树的概念与实现

目录 ​一、红黑树的概念 1.什么是红黑树 2.红黑树满足的性质 3.红黑树存在的意义 二、红黑树的实现 1.类的构建 2.插入函数 &#xff08;1&#xff09;插入一个节点 &#xff08;2&#xff09;调整节点 &#xff08;3&#xff09;旋转 三、红黑树的检验 一、红黑树…

okio篇2-RealBufferedSource

上一篇讲过&#xff0c;okio只有两个概念&#xff0c;source和sink。source对应InputStream&#xff0c;即负责将数据读出&#xff0c;是一个输出方&#xff08;所以只有source.read方法&#xff09;。sink对应outputStream&#xff0c;负责获取数据写入&#xff0c;是一个写入…

RT-Thread Nano在keil Simulator中的仿真

目的&#xff1a;使用STM32CubeMX生成包含RT-Thread Nano内核和FinSH控制台的keil工程&#xff0c;在没有硬件开发板的情况下&#xff0c;通过keil Simulator来运行系统&#xff0c;并通过SHELL来与系统进行交互。 一、使用STM32CubeMX生成RT-Thread Nano工程 官方文档已经说…

C++标准库 -- 动态内存 (Primer C++ 第五版 · 阅读笔记)

C标准库 --动态内存 (Primer C 第五版 阅读笔记&#xff09; 第12章 动态内存------(持续更新)12.1、动态内存与智能指针12.1.1、shared_ptr类12.1.2、直接管理内存12.1.3、shared_ptr和new结合使用12.1.4、智能指针和异常12.1.5、unique_ptr12.1.6、weak_ptr 12.2、动态数组1…

网络通信之网络层与数据链路层

文章目录 讲在前面网络层网络层概述IP协议格式网段划分公有IP、私有IP、特殊IP理解路由 数据链路层MAC地址以及MAC帧&#xff08;以太网帧&#xff09;MTU协议MTU对IP和TCP协议的影响ARP协议及其作用 涉及到的相关协议DNS协议&#xff08;应用层&#xff09;NAT与NAPT协议 总结…

BEV (0)---DETR

1 DETR 1.1 DETR处理流程 1.1.1 将图像输入给Backbone获得图像特征与位置编码 ①. 对给定的输入图像通过resnet进行特征提取&#xff0c;最终得到特征图C5∈RBx2048xhxw,其中h、w为输入图像尺寸得1/32。随后再用一层11卷积压缩一下通道&#xff0c;得到特征图P5∈RBx256xhxw。…

jvm调优策略

jvm调优主要是内存管理方面的调优&#xff0c;包括各个代的大小&#xff0c;GC策略等。 代大小调优 JVM 中最大堆大小有三方面限制&#xff1a;相关操作系统的数据模型&#xff08;32-bt还是64-bit&#xff09;限制&#xff1b;系统的可用虚拟内存限制&#xff1b;系统的可用物…

数据结构学习记录——什么是堆(优先队列、堆的概念、最大堆最小堆、优先队列的完全二叉树表示、堆的特性、堆的抽象数据类型描述)

目录 优先队列 若采用数组或链表实现优先队列 数组 链表 有序数组 有序链表 总结 若采用二叉搜索树来实现优先队列 最大堆 堆的概念 优先队列的完全二叉树表示 堆的两个特性 结构性 有序性 【例】最大堆和最小堆 【例】不是堆 堆的抽象数据类型描述 优先队列…

安排超市 -- BFS分割搜索

4.安排超市 给定一个n*n的地图。地图是上下左右四联通的&#xff0c;不能斜向行走&#xff1a; *代表障碍&#xff0c;不可通行。 .代表路&#xff0c;可以通行。 #代表房子。房子也是可以通行的。 小红现在需要在一些地方安排一些超市&#xff08;不能安排在障碍物上&#xf…

山东专升本计算机第七章-计算机网络基础

计算机网络基础 计算机网络系统 考点 6 计算机网络硬件 主体设备 • 称为主机 • 一般可分为中心站&#xff08;又称服务器&#xff09;和工作站&#xff08;客户机&#xff09; 连接设备 • 网卡 • 工作在数据链路层 • 网卡又称网络适配器&#xff0c;是连接主机和网…

【C++初阶】引用

一.概念 引用就是取别名&#xff0c;在语法上它不会开空间&#xff0c;而是和它引用的变量共用同一块空间。对引用的操作也就是对原来变量的操作。就像现实生活中给人取外号一样&#xff0c;不管是喊外号还是本名&#xff0c;指的都是那个人。 二.引用特性 1.引用类型必须和引用…

Java8 新特性讲解

一、Lambda表达式 Lambda 是一个匿名函数&#xff0c;我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格&#xff0c;使Java的语言表达能力得到了提升。 二、函数式接口 &#…

【网课平台】Day15.Devops:持续集成与持续交付

文章目录 一、Devops1、什么是Devops2、什么是CI/CD3、Devops方案参考 二、人工部署1、项目打jar包2、生成镜像、创建容器 三、自动化部署1、代码提交到git2、修改pom.xml文件3、前端部署 一、Devops 1、什么是Devops 一个软件的生命周期包括&#xff1a;需求分析阶、设计、开…

SpringCloud:ElasticSearch之集群

单机的elasticsearch做数据存储&#xff0c;必然面临两个问题&#xff1a;海量数据存储问题、单点故障问题。 海量数据存储问题&#xff1a;将索引库从逻辑上拆分为N个分片&#xff08;shard&#xff09;&#xff0c;存储到多个节点单点故障问题&#xff1a;将分片数据在不同节…

【原创】运维的终点是开发~chatGPT告诉你真相

文章目录 软件技术岗位鄙视链&#xff0c;你在哪层呢&#xff1f;让chatGPT告诉你运维工作好&#xff0c;还是开发工作好问它几个问题1. 一个三年运维成长的案例和薪资2. 一个三年开发成长的案例和薪资3. 一个五年运维成长的案例和薪资4. 一个五年开发成长的案例和薪资5. 一个十…

云分析迁移:顺应需求

云提供了对新分析功能、工具和生态系统的访问&#xff0c;可以快速利用这些功能、工具和生态系统来测试、试点和推出新产品。然而&#xff0c;尽管迫在眉睫&#xff0c;但企业在将分析迁移到云时仍感到担忧。组织正在寻找能够帮助他们分配资源和集成业务流程的服务提供商&#…

Linux 服务器上安装和使用 Redis,只需这 4 步!

一、使用 yum 安装 Redis 使用以下命令&#xff0c;直接将 redis 安装到 linux 服务器&#xff1a; yum -y install redis 二、配置远程连接 a&#xff09;首先第一步&#xff0c;将 redis 配置文件下载到本地&#xff08;如果你熟悉 vim 操作&#xff0c;直接用 vim 编辑即可…

论文阅读《PIDNet: A Real-time Semantic Segmentation Network Inspired by PID》

论文地址&#xff1a;https://arxiv.org/pdf/2206.02066.pdf 源码地址&#xff1a;https://github.com/XuJiacong/PIDNet 概述 针对双分支模型在语义分割任务上直接融合高分辨率的细节信息与低频的上下文信息过程中细节特征会被上下文信息掩盖的问题&#xff0c;提出了一种新的…

【五一创作】Springboot+多环境+多数据源(MySQL+Phoenix)配置及查询(多知识点)

文章目录 1. 背景2. 技术点3 子模块依赖SpringBoot设置4. 多环境配置4.1 application.yml4.2 application-pro.yml 5. 多数据源配置5.1 yml配置5.2 自定义数据源在Java中配置5.2.1 PhoenixDataSourceConfig5.2.2 MysqlDataSourceConfig 6. 完整的Pom6. 测试6.1 Mapper配置6.2 方…