【数据结构与算法】 - 时间复杂度和空间复杂度、二分查找、线性查找

news2024/11/17 10:59:57

数据结构与算法

    • 1. 数据结构的定义
    • 2. 二分查找
      • 2.1 二分查找的定义
      • 2.2 二分查找分析
      • 2.3 二分查找实现
      • 2.4 二分查找算法图解
      • 2.5 二分算法引发的问题
      • 2.6 二分算法改良版
      • 2.7 二分算法改良版解析
      • 2.8 二分算法改良版图解
      • 2.9 二分算法改良版注意事项
    • 3. 时间复杂度
      • 3.1 时间复杂度的概念
      • 3.2 线性查找的时间复杂度
      • 3.3 二分算法的时间复杂度
      • 3.4 不同的时间复杂度对比
    • 4. 空间复杂度
      • 4.1 空间复杂度概念
      • 4.2 空间复杂度的计算

1. 数据结构的定义

在计算机科学领域,数据结构是一种数据组织、管理和存储格式,通常被选择用来高效访问数据。

2. 二分查找

2.1 二分查找的定义

二分查找算法也称折半查找,是在一个升序数组中,查找要需要找的值,如果找到就返回该数的索引,否则返回-1

2.2 二分查找分析

(1) 定义两个变量分别是i和j,i默认的索引为0,j默认的索引为指定数组的最后一个元素的索引
(2) 如果i>j,说明没找到该元素
(3) 定义一个变量m,m为中间索引
(4) 如果target < a[m],j = m-1
(5)如果a[m] < target,i = m+1
(6)如果 a[m] = target,说明找到,返回该值的索引

2.3 二分查找实现

 // 二分查找 - 基础版
    public static int binarySearch(int a[], int target) {
        int i = 0;
        int j = a.length - 1;
        while (i <= j) {
            int m = (i + j) / 2;
            if (a[m] < target) {
                i = m + 1;
            } else if (target < a[m]) {
                j = m - 1;
            } else {
                return m;
            }
        }
        return -1;
    }

2.4 二分查找算法图解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.5 二分算法引发的问题

问题1:为什么是 i <= j ? 而不是 i < j呢?

因为当i == j,i,j 它们共同指向的元素也会参与比较
如果是i < j,意味着只有m指向的元素参与比较
这样可能会造成有些元素在数组中无法找到

问题2:(i + j) / 2 会产生的问题

因为当二分查找时,一个数组的元素足够的多时,
(i + j) / 2 会超过了整数的最大值。

给大家举个例子:

    @Test
    public void test4() {
        int i = 0;
        int j = Integer.MAX_VALUE - 1;
        System.out.println("整数最大值:" + Integer.MAX_VALUE);
        int m = (i + j) / 2;
        i = m + 1;
        System.out.println("i = " + i);
        System.out.println("j = " + j);
        System.out.println(i + j);
        // 出现这一现象的原因是,两数相加超过了整数的最大值
    }

为什么两个整数相加会出现负数呢?

在这里插入图片描述

这是因为int类型的整数的最大值是2147483647,这两个数相加已经超出了一个int类型的数的最大值,所以会出现负数,又因为Java中没有无符号数,所有数都是有符号数,所以会出现这种情况

在这里插入图片描述

解决方法:采用无符号右移 >>> ,不保留符号位右移,高位补0

public static int binarySearchBasic(int[] a, int target) {
        int i = 0, j = a.length - 1;
        while (i <= j) {
            int m = (i + j) >>> 1;   // 这里与之前相比采用 >>> 来代替除号
            if (a[m] < target) {
                i = m + 1;
            }
            else if (target < a[m]) {
                j = m - 1;

            } else {
                return m;
            }
        }
        return -1;
    }

2.6 二分算法改良版

基于上面的二分计算基础版,还可以再改善

public static int binarySearchAlternative(int[] a, int target) {
        int i = 0, j = a.length;
        while (i < j) {
            int m = (i + j) >>> 1;
            if (a[m] < target) {
                // 此时i只是作为边界,而不参与运算
                i = m + 1;
            } else if (target < a[m]) {
                j = m;
            } else {
                return m;
            }
        }
        return -1;
    }

2.7 二分算法改良版解析

假如在数组中查找元素14,这时j指向的是数组的最后一个元素的索引加1,说的简单点,这个 j 指向的是边界

    public static int binarySearchAlternative(int[] a, int target) {
        int i = 0;
        int j = a.length;  // 与之前不同
        while (i < j) {  // 与之前不同
            int m = (i + j) >>> 1;
            if (a[m] > target) {
                j = m; // 与之前不同 
            } else if (a[m] < target) {
                i = m + 1;
            } else {
                return m;
            }
        }
        return -1;
    }

2.8 二分算法改良版图解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注意:此时j不参与运算

2.9 二分算法改良版注意事项

这里如果是 i<=j 并且查找的元素不在数组中会造成无限循环,前面也说过了,j指向的元素是边界,所以i=j时说明要查找的元素不在该数组中

    public static int binarySearchAlternative(int[] a, int target) {
        int i = 0, j = a.length;
        while (i <= j) {
            int m = (i + j) >>> 1;
            if (a[m] < target) {
                i = m + 1;
            } else if (target < a[m]) {
                j = m;
            } else {
                return m;
            }
        }
        return -1;
    }

3. 时间复杂度

请添加图片描述

3.1 时间复杂度的概念

时间复杂度是用来衡量:一个算法的执行,随数据规模增大,而增长的时间成本,利用大O来表示

  • 如果运行时间是常数量级, 则用常数1表示
  • 只保留间中的最高阶项
  • 如果最高阶项存在, 则省去最高阶项前面的系数

3.2 线性查找的时间复杂度

    public static int linearSearch(int[] a, int target) {
        for (int i = 0; i < a.length; i++) {
            if (a[i] == target) {
                return i;
            }
        }
        return -1;
    }

要计算一个算法的时间复杂度,就要考虑该算法的最坏情况,假设该数组中有n个元素

int i = 0;    1次
i < a.length; n + 1次
i++           n次
a[i]==target  n次
return -1;    1次
3 * n + 3  
  • 粗略认为每行代码执行时间是 t t t,假设 n = 4 n=4 n=4 那么总执行时间是 ( 1 + 4 + 1 + 4 + 4 + 1 ) ∗ t = 15 t (1+4+1+4+4+1)*t = 15t (1+4+1+4+4+1)t=15t
  • 可以推导公式为, T = ( 3 ∗ n + 3 ) t T = (3*n+3)t T=(3n+3)t

然后只保留最高阶项,再去掉最高阶项的系数
所以它的时间复杂度就是 O ( n ) O(n) O(n)

3.3 二分算法的时间复杂度

  考虑二分查找最坏的情况
  1 [2,3,4,5] 5 右侧没找到时,情况更差
  
  int i = 0,j = a.length-1;         2
  return -1;                        1
  元素个数              循环次数
  4-7                   3           floor(log_2(4))  = 2+1
  8-15                  4           floor(log_2(8))  = 3+1
  16-31                 5           floor(log_2(16)) = 4+1
  32-63                 6           floor(log_2(32)) = 5+1
  ....                 ...

  循环次数:floor(log_2(n)) + 1 , 我们把它看成L
  这里使用到了floor()方法,是因为当元素个数小于4的时就向下取整
  i<=j                  L+1
  int m = (i+j)>>>1     L
  a[m] < target         L
  target < a[m]         L
  i = m+1               L

  ((floor(log_2(n)) + 1) * 5) + 4     ->  最糟糕的情况

这里也是只保留最高项,然后去掉底数所以它的时间复杂度为 l o g ( n ) log(n) log(n)

3.4 不同的时间复杂度对比

在这里插入图片描述
从上图中不难看出,当数据量足够大的时候,所消耗的时间为: O ( 1 ) O(1) O(1) < O ( l o g n ) O(logn) O(logn)< O ( n ) O(n) O(n)< O ( n 2 ) O(n^2) O(n2)< O ( n 3 ) O(n^3) O(n3)

4. 空间复杂度

4.1 空间复杂度概念

与时间复杂度类似,一般也使用大 O O O 表示法来衡量:一个算法执行随数据规模增大,而增长的额外空间成本

4.2 空间复杂度的计算

public static int binarySearchBasic(int[] a, int target) {
    int i = 0, j = a.length - 1;    
    while (i <= j) {               
        int m = (i + j) >>> 1;
        if(target < a[m]) {        
            j = m - 1;
        } else if (a[m] < target) { 
            i = m + 1;
        } else {                    
            return m;
        }
    }
    return -1;
}

这里因为是3个变量, i ,j , m ,一个int是4个字节
3 * 4B = 12B,同上面的时间复杂度,当它只有一个常数项时,它的空间复杂度为 O ( 1 ) O(1) O(1)

在这里插入图片描述

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

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

相关文章

数据计算-第15届蓝桥杯第一次STEMA测评Scratch真题精选

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第154讲。 第15届蓝桥杯第1次STEMA测评已于2023年8月20日落下帷幕&#xff0c;编程题一共有6题&#xff0c;分别如下&a…

ThreeJS-3D教学二:基础形状展示

three中提供了22 个基础模型&#xff0c;此案例除了 EdgesGeometry、ExtrudeGeometry、TextGeometry、WireframeGeometry&#xff0c;涵盖 17 个形状。 Fog 雾化设置&#xff0c;这是scene场景效果EdgesGeometry , WireframeGeometry 更多地可能作为辅助功能去查看几何体的边和…

修炼k8s+flink+hdfs+dlink(一:安装flink)

一&#xff1a;standalone的ha环境部署。 创建目录&#xff0c;上传安装包。 mkdir /opt/app/flink 上传安装包到本目录。 tar -zxvf flink-1.13.6-bin-scala_2.12.tgz配置参数。 在flink-conf.yaml中添加zookeeper配置 jobmanager.rpc.address: node01 high-availability: …

论文浅尝 | INGRAM:通过关系图的归纳知识图谱嵌入

笔记整理&#xff1a;郭荣辉&#xff0c;天津大学硕士 链接&#xff1a;https://arxiv.org/abs/2305.19987 动机 归纳知识图谱补全是预测训练期间没有观察到的新实体之间缺失的三元组的任务。虽然大多数归纳知识图谱补全方法假定所有实体都是新的&#xff0c;但它们不允许在推理…

【c语言的malloc函数介绍】

malloc&#xff08;memory allocation的缩写&#xff09;是C语言中的一个函数&#xff0c;用于动态分配内存空间。这个函数允许你在程序运行时请求指定大小的内存块&#xff0c;以供后续使用。malloc函数属于标准库函数&#xff0c;需要包含头文件#include <stdlib.h> 才…

使用Vue、ElementUI实现登录注册,配置axios全局设置,解决CORS跨域问题

目录 引言 什么是ElementUI&#xff1f; 步骤1&#xff1a;创建Vue组件用于用户登录和注册 1. 基于SPA项目完成登录注册 在SPA项目中添加elementui依赖 在main.js中添加elementui模块 创建用户登录注册组件 配置路由 修改项目端口并启动项目 静态页面展示图 步骤2&#x…

搭建qml box2d开发环境

box2d是开源的优秀物理引擎 box2d官网 https://box2d.org/ qml box2d插件工程 https://gitee.com/gao_yao_yao/qml-box2d 1. qml box2d插件工程 下载&#xff0c;解压qml-box2d-master.zip&#xff0c;用qt打开box2d.pro&#xff0c;编译Debug|Release拷贝Box2D.dll|Box2Dd.…

LeetCode算法二叉树—226. 翻转二叉树

目录 226. 翻转二叉树 代码&#xff1a; 运行结果&#xff1a; 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&#xff1a; 输入…

easyrecovery好用吗 easyrecovery软件收费吗

EasyRecovery是一款专业的数据恢复软件&#xff0c;它功能强大且性价比高&#xff0c;能够精确找回需要的文件&#xff0c;方便又快捷。那么Easyrecovery好用吗&#xff0c;Easyrecovery软件收费吗。今天我为大家解答一下这两个问题。 一、Easyrecovery好用吗 EasyRcovery可用…

系统接口响应信息通用加密设计

设计目的 出于对一些敏感信息的安全性考虑&#xff0c;接口的响应信息需要进行加密&#xff0c;避免明文传输 使用场景 本系统前端响应信息加密 第三方系统响应信息加密 功能设计思路 配置模式加密 使用场景&#xff1a;本系统前端响应信息加密 在nacos中配置需要加密的…

用于生物分子修饰的Alkyne NHS ester,906564-59-8

产品简介&#xff1a;用于生物分子修饰的炔烃NHS酯。铜催化的化学反应中的炔基几乎从未在天然分子中遇到过。然而&#xff0c;这种NHS酯允许将炔基连接到氨基上&#xff0c;氨基在自然界中普遍存在&#xff0c;存在于蛋白质、肽、合成氨基DNA和许多小分子中。炔基随后可以通过铜…

langchain+gpt+agent

一.agentConversation 通过用户问题&#xff0c;来选择 import json import os import refrom langchain import FAISS, PromptTemplate, LLMChain from langchain.agents import initialize_agent, Tool, AgentType from langchain.chains import RetrievalQA from langchai…

C++ | C++11新特性(下)

前言 前面我们介绍了C11列表初始化、新的类功能以及右值引用等新特性&#xff0c;本文继续介绍关于可变参数模板以及lambda表达式等新语法&#xff1b; 一、可变参数模板 在C11前&#xff0c;我们有普通固定数量模板参数&#xff0c;但对于可变参数&#xff0c;我们无从下手&am…

淘宝电商必备的大数据应用

在日常生活中&#xff0c;大家总能听到“大数据”“人工智能”的说法。现在的大数据技术应用&#xff0c;从大到巨大科学研究、社会信息审查、搜索引擎&#xff0c;小到社交联结、餐厅推荐等等&#xff0c;已经渗透到我们生活中的方方面面。到底大数据在电商行业可以怎么用&…

什么是EventEmitter?它在Node.js中有什么作用?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是EventEmitter&#xff1f;⭐ 它在Node.js中的作用是什么&#xff1f;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为…

轻松上手Docker:学习如何创建和运行自己的Docker容器

文章目录 轻松上手Docker&#xff1a;学习如何创建和运行自己的Docker容器容器的介绍Docker的技术架构容器的工作机制&#xff08;Docker&#xff09;容器的关键技术 - NamespaceNamespace隔离说明 容器的关键技术 - CgroupDocker环境搭建1&#xff09;安装基础软件包2&#xf…

分类预测 | Matlab实现SSA-CNN-SVM麻雀算法优化卷积支持向量机分类预测

分类预测 | Matlab实现SSA-CNN-SVM麻雀算法优化卷积支持向量机分类预测 目录 分类预测 | Matlab实现SSA-CNN-SVM麻雀算法优化卷积支持向量机分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现SSA-CNN-SVM麻雀算法优化卷积支持向量机分类预测&#xff0…

跟着顶级科研报告IPCC学绘图:温度折线/柱图/条带/双y轴

复现IPCC气候变化过程图 引言 升温条带Warming stripes&#xff08;有时称为气候条带&#xff0c;目前尚无合适且统一的中文释义&#xff09;是数据可视化图形&#xff0c;使用一系列按时间顺序排列的彩色条纹来视觉化描绘长期温度趋势。 在IPCC报告中经常使用这一方案 IPCC是…

自学——网络安全——黑客技术

想自学网络安全&#xff08;黑客技术&#xff09;首先你得了解什么是网络安全&#xff01;什么是黑客&#xff01;&#xff01;&#xff01; 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队…

【小程序 - 基础】WXML、WXSS语法以及小程序的配置、网络数据请求_03

目录 一、WXML模板语法 1. 数据绑定 1.1 数据绑定的基本原则 1.2 在 data 中定义页面的数据 1.3 Mustache 语法的格式 1.4 Mustache 语法的应用场景 2. 事件绑定 2.1 什么是事件 2.2 小程序中常用的事件 2.3 事件对象的属性列表 2.4 target 和 currentTarget 的区别…