Hello 算法10:搜索

https://www.hello-algo.com/chapter_searching/binary_search/

二分查找法

给定一个长度为 n的数组 nums ,元素按从小到大的顺序排列,数组不包含重复元素。请查找并返回元素 target 在该数组中的索引。若数组不包含该元素,则返回 -1 。

# 首先初始化 i=0,j=n-1, 代表搜索区间是[0,n-1]
# 然后,循环执行以下2个步骤
# 1:m = (i+j)/2 ,向下取整,求出搜索区间的中间点
# 2:判断nums[m]和target的大小关系,有以下三种情况:
#    a:nums[m] > target,说明目标在区间[i,m-1],所以让j = m - 1
#    b: nums[m] < target,说明目标在区间[m+1,j],所以让i = m + 1
#    c:说明已经找到目标值,因此返回索引m

代码如下:

def binary_search(nums: list[int], target: int):
    i, j = 0, len(nums) - 1
    while i <= j:
        m = (i+j) // 2
        if nums[m] > target:
            j = m -1
        elif nums[m] < target:
            i = m + 1
        else:
            return m
    return -1

优点:效率高,无需额外空间

缺点:仅适用于有序数据,仅使用数数组搜索,当数据量较小时,线性查找速度更快。

二分查找插入点

给定一个长度为 n的有序数组 nums 和一个元素 target ,数组不存在重复元素。现将 target 插入到数组 nums 中,并保持其有序性。若数组中已存在元素 target ,则插入到其左方。请返回插入后 target 在数组中的索引。

  1. 当target存在时,插入的索引就是taget的位置
  2. 当target不存在时:如果target > nums[m],让i = m +1 ,所以i在靠着大于等于目标的位置移动;反之j在靠着小于等于目标的位置移动,这导致的结果就是,最终i等于第一个比目标大的元素,j指向首个比目标小的元素。

可知,最终返回i即是插入的位置

def binary_search_insertion_simple(nums: list[int], target: int) -> int:
    """二分查找插入点(无重复元素)"""
    i, j = 0, len(nums) - 1  # 初始化双闭区间 [0, n-1]
    while i <= j:
        m = (i + j) // 2  # 计算中点索引 m
        if nums[m] < target:
            i = m + 1  # target 在区间 [m+1, j] 中
        elif nums[m] > target:
            j = m - 1  # target 在区间 [i, m-1] 中
        else:
            return m  # 找到 target ,返回插入点 m
    # 未找到 target ,返回插入点 i
    return i

重复值的情况

在上一题的基础上,规定数组可能包含重复元素,其余不变

def binary_search_insertion(nums: list[int], target: int) -> int:
    """二分查找插入点(存在重复元素)"""
    i, j = 0, len(nums) - 1  # 初始化双闭区间 [0, n-1]
    while i <= j:
        m = (i + j) // 2  # 计算中点索引 m
        if nums[m] < target:
            i = m + 1  # target 在区间 [m+1, j] 中
        elif nums[m] > target:
            j = m - 1  # target 在区间 [i, m-1] 中
        else:
            j = m - 1  # 首个小于 target 的元素在区间 [i, m-1] 中
    # 返回插入点 i
    return i

查找左边界

def binary_search_left_edge(nums: list[int], target: int) -> int:
    """二分查找最左一个 target"""
    # 等价于查找 target 的插入点
    i = binary_search_insertion(nums, target)
    # 未找到 target ,返回 -1
    if i == len(nums) or nums[i] != target:
        return -1
    # 找到 target ,返回索引 i
    return i

查找右边界

替换在 nums[m] == target 情况下的指针收缩操作即可,接下来介绍一些取巧的办法

  1. 复用左边界法,使查找目标加一

    def binary_search_right_edge(nums: list[int], target: int) -> int:
        """二分查找最右一个 target"""
        # 转化为查找最左一个 target + 1
        i = binary_search_insertion(nums, target + 1)
        # j 指向最右一个 target ,i 指向首个大于 target 的元素
        j = i - 1
        # 未找到 target ,返回 -1
        if j == -1 or nums[j] != target:
            return -1
        # 找到 target ,返回索引 j
        return j
    
  2. 转换为查找不存在的元素

    当数组不包含目标元素时,最终i和j会分别指向首个大于、小于target的元素:

    查找最左侧元素时,可以将目标设置为targe-0.5,最终返回i

    查找最右侧元素时,可以将目标设置为target+0.5,最终返回j

    在这里插入图片描述

哈希优化

在算法题中,通常通过将线性遍历替换为哈希搜索来提升时间复杂度。例如以下题目

给定一个整数数组 nums 和一个目标元素 target ,请在数组中搜索“和”为 target 的两个元素,并返回它们的数组索引。返回任意一个解即可。

线性遍历

开启一个两层循环,每次判断是否和为目标值。简单粗暴

def two_sum_brute_force(nums: list[int], target: int) -> list[int]:
    """方法一:暴力枚举"""
    # 两层循环,时间复杂度为 O(n^2)
    n = len(nums)
    for i in range(n):
        for j in range(i+1, n):
            if nums[i] + nums[i] == target:
                return [i, j]
    return []

哈希查找

def two_sum_hash_table(nums: list[int], target: int) -> list[int]:
    """方法二:辅助哈希表"""
    # 辅助哈希表,空间复杂度为 O(n)
    dic = {}
    n = len(nums)
    for i in range(n):
        if target - nums[i] not in dic:
            dic[nums[i]] = i
        else:
            return [dic[target - nums[i]], i]
    return []

搜索算法总结

搜索算法根据实现方式可以分为以下两类:

  • 通过遍历数据结构来定位元素,例如数组、图、树的遍历等
  • 利用数据结构的特性,实现高效搜索,例如二分查找、哈希查找

暴力搜索

  • 线性搜索,适用于数组、链表
  • 广度优先和深度优先搜索,适用于图、树

优点是通用性好,容易理解,不需要对数据结构做预期处理;不需要额外空间。

缺点是此类算法的时间复杂度为O(n),因此在元素较多时效率较低

自适应搜索

自适应搜索利用数据结构的特性来优化搜索

  • 二分查找,利用有序性来进行搜索,仅适用于数组
  • 哈希查找,利用哈希表将搜索数据和目标数据建立键值对映射,从而实现查询操作
  • 树查找

效率高,可达到o(logn)甚至o(1)

缺点:需要对数据进行预处理,需要额外空间

搜索方法选取

在这里插入图片描述

表 10-1 查找算法效率对比

线性搜索二分查找树查找哈希查找
查找元素O(n)O(log⁡n)O(log⁡n)O(1)
插入元素O(1)O(n)O(log⁡n)O(1)
删除元素O(n)O(n)O(log⁡n)O(1)
额外空间O(1)O(1)O(log⁡n)O(n)
数据预处理/排序 O(nlog⁡n)建树 O(nlog⁡n)建哈希表 O(n)
数据是否有序无序有序有序无序

搜索算法的选择还取决于数据体量、搜索性能要求、数据查询与更新频率等。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu 22.04 开机自动挂载webdav - 设置开机自启脚本 - 解决坚果云webdav无写入权限

效果图&#xff1a; 前言&#xff1a; 1&#xff09;亲测/etc/fstab的办法没有成功自动挂载&#xff0c;换成传统的rc.local可以解决&#xff1b; 2&#xff09;rc-local.service是系统自带的一个开机自启服务&#xff0c;但是在 ubuntu 20.04 上&#xff0c;该服务默认没有开…

基于物理原理的p-GaN HEMT动态导通电阻SPICE建模

来源&#xff1a;Physics-Based SPICE Modeling of Dynamic ON-State Resistance of p-GaN HEMTs&#xff08;TPEL 23年&#xff09; 摘要 这封快报介绍了一种新型基于物理学原理的SPICE建模方法&#xff0c;专门针对氮化镓基p型门极高电子迁移率晶体管&#xff08;p-GaN HEM…

route路由命令、ip route命令、default默认路由(0.0.0.0 )

文章目录 概述3. route语法3.1 查看路由表3.1 参数解释 3.2 添加路由记录3.2.1 添加到达单个目标主机的路由3.2.2 添加到达目标网络的路由3.2.3 添加默认路由 3.3 删除路由记录 4. ip route4.1 查看路由4.1.1 不带条件4.1.2 带条件4.1.3 字段解释4.1.3 字段解释 4.2 添加路由4.…

基于Springboot+Vue的Java项目-高校心理教育辅导系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

发布 Chrome/Edge浏览器extension扩展到应用商店

Chrom Extension发布流程 创建和发布自定义 Chrome 应用和扩展程序&#xff1a;https://support.google.com/chrome/a/answer/2714278?hlzh-Hans 在 Chrome 应用商店中发布&#xff1a;https://developer.chrome.com/docs/webstore/publish?hlzh-cn 注册开发者帐号&#…

Eclipse+Java+Swing实现图书信息管理系统-TXT存储信息

一、系统介绍 1.开发环境 操作系统&#xff1a;Win10 开发工具 &#xff1a;Eclipse2021 JDK版本&#xff1a;jdk1.8 存储方式&#xff1a;Txt文件存储 2.技术选型 JavaSwingTxt 3.功能模块 4.工程结构 5.系统功能 1.系统登录 管理员可以登录系统 2.查看图书 管理员…

软件无线电安全之HackRF One初探

HackRF介绍 HackRF是一款开源软件无线电&#xff08;SDR&#xff09;平台&#xff0c;由Great Scott Gadgets公司推出。它具有广泛的频率覆盖范围&#xff0c;从1 MHz到6 GHz&#xff0c;支持大部分常见的无线通信频段。采用软件定义无线电技术&#xff0c;HackRF提供了自定义…

vue快速入门(二十四)输入停顿再进行响应

注释很详细&#xff0c;直接上代码 上一篇 新增内容 使用侦听器监视数据变化情况使用clearTimeout与定时器实现停顿一段时间再操作内容 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"view…

【王道数据结构笔记】顺序表的动态分配代码分析

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;数据结构 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…

docker 安装 nginx + httpd + php-fpm

原文地址&#xff1a;http://www.taoluyuan.com/index.php/archives/30/#2 展示 1.安装 1.1安装docker 1.2安装nginx 1.3安装apache-httpd 1.4安装php-fpm 2.配置nginx反向代理 httpdphp-fmp 1.安装 1.1安装docker 移除旧的版本&#xff1a; sudo yum remove docker 安装…

redis-plus-plus的安装与使用

本文参考自 redis-plus-plus 官方文档 一、安装 因为redis-plus-plus是基于hiredis封装的&#xff0c;所以需要先安装hiredis&#xff1b; 第一步&#xff1a;安装hiredis # 使用git下载源代码 git clone https://github.com/redis/hiredis.git # 进入源代码主目录 cd hired…

ChatGPT在线网页版

ChatGPT镜像 今天在知乎看到一个问题&#xff1a;“平民不参与内测的话没有账号还有机会使用ChatGPT吗&#xff1f;” 从去年GPT大火到现在&#xff0c;关于GPT的消息铺天盖地&#xff0c;真要有心想要去用&#xff0c;途径很多&#xff0c;别的不说&#xff0c;国内GPT的镜像…

LangChain LangServe 学习笔记

LangChain LangServe 学习笔记 0. 引言1. LangServe 概述2. 特性3. 限制4. 安装5. 示例应用程序6. OpenAPI文档7. Python SDK 客户端8. Playground9. 聊天可运行页面 0. 引言 使用 LangServe 可以立即将您的LLM应用程序变成 API 服务器。 LangServe 使用 FastAPI 构建&#x…

5. Mysql的binlog介绍

参考&#xff1a;InnoDB学习&#xff08;三&#xff09;之BinLog 1. BinLog介绍 BinLog又称为二进制日志&#xff0c;是MySQL服务层的数据日志&#xff0c;MySQL所有的存储引擎都支持BinLog。 BinLog记录了MySQL中的数据更新和可能导致数据更新的事件&#xff0c;可以用于主从…

大数据深度学习:基于Tensorflow深度学习卷积神经网络CNN算法垃圾分类识别系统

文章目录 大数据深度学习&#xff1a;基于Tensorflow深度学习卷积神经网络CNN算法垃圾分类识别系统一、项目概述二、深度学习卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;简称CNN&#xff09;三、部分数据库架构四、系统实现系统模型部分核心代码模型训…

【C++】模板初阶——泛型编程、函数模板、类模板

1. 泛型编程 如何实现一个通用的交换函数呢&#xff1f; void Swap(int& left, int& right) {int temp left;left right;right temp; } void Swap(double& left, double& right) {double temp left;left right;right temp; } void Swap(char& left…

双向链表的实现(详解)

目录 前言初始化双向链表的结构为双向链表的节点开辟空间头插尾插打印链表尾删头删查找指定位置之后的插入删除pos节点销毁双向链表 前言 链表的分类&#xff1a; 带头 不带头 单向 双向 循环 不循环 一共有 (2 * 2 * 2) 种链表 带头指的是&#xff1a;带有哨兵位节点 哨兵位&a…

关于部署ELK和EFLK的相关知识

文章目录 一、ELK日志分析系统1、ELK简介1.2 ElasticSearch1.3 Logstash1.4 Kibana&#xff08;展示数据可视化界面&#xff09;1.5 Filebeat 2、使用ELK的原因3、完整日志系统的基本特征4、ELK的工作原理 二、部署ELK日志分析系统1、服务器配置2、关闭防火墙3、ELK ElasticSea…

个人笔记目录

目录 一、lora 微调 alpaca 笔记 二、全量微调 Llama2-7b笔记 三、Huggingface trainer 与 from_pretrained简单介绍&#xff08;笔记&#xff09; 四、vscode调试launch.json常用格式 五、huggingface generate函数简介 六、Trl: llama2-7b-hf使用QLora 4bit量化后ds zer…

自动化收集Unity版本更新日志

自动化收集Unity版本更新日志 &#x1f365;功能介绍&#x1f96a;食用手册填写配置开始搜集 &#x1f368;数据展示 &#x1f365;功能介绍 &#x1f4a1;获取指定年份中所有的Unity版本更新日志。 &#x1f4a1;根据指定字符串过滤。 &#x1f4a1;.收集后自动保存成markdow…