数组中的算法

news2024/11/26 11:01:07

目录

1.什么是数组

2.数组上的算法

2.1二分查找算法

什么是二分查找算法?

算法步骤

算法时间复杂度

一个问题

例题

题目分析

解题代码   

2.2双指针法

什么是双指针法?

例题

题目分析

解题代码


1.什么是数组

数组是在一块连续的内存空间上存储相同类型数据的集合。其实数组也是一种基本的数据结构,也是很多更加高级的数结构的基础,比如 栈、队列、哈希表……都可以基于数组实现。

下图展示了一个长度为12的 int 类型的数组:

需要注意的是: 

  • 数组的下标是从0开始计数的。
  • 数组在内存空间中的地址是连续的。

2.数组上的算法

因为数组的要求是一块连续的内存空间,所以我们可以利用其连续的特性玩一些算法。

2.1二分查找算法

什么是二分查找算法?

二分查找算法(Binary Search Algorithm)是一种在有序数组中查找某一特定元素的搜索算法。

算法步骤

  • 定义左右两个指针。(这里的指针是形象的说法,代表数字的下标)
  • 计算出中间元素的下标,判断待查找元素与数组中间元素的大小。
  • 如果该特定元素小于中间元素,则在中间元素的左边的区间进行查找;需要更新右指针的值。
  • 如果该特定元素大于中间元素,则在中间元素的右边的区间进行查找;需要更新左指针的值。

算法时间复杂度

这种搜索算法每一次比较都使搜索范围缩小一半,因此其时间复杂度为O(log n),其中n是数组的长度。

一个问题

整个查找过程在一个循环中进行,在未找到指定元素的情况下,左指针不断向右移,右指针不断向左移,那么循环的结束条件是什么呢?是left < right 还是 left <= right 呢?

怎么写这个循环的结束条件,主要取决于left == right 的情况是否有意义,有意义就写成 <= ,没有意义就写成 < ;那么 left == right 到底有没有意义,主要取决于right指针的定义,right指针的定义决定了待查找的区间是 左闭右闭 还是 左闭右开 的。

例题

题目链接 —— 二分查找  (题目来源于力扣)

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

题目分析

题目表明给定的数组是有序的,并且是升序,也就是说数组中没有重复的元素,因此查找到的下标是唯一的,我们可以考虑使用二分查找算法。

解题代码   

写法一:定义左闭右闭区间。当我们定义区间为左闭右闭时,left == right 这种情况是有效的。

// 定义左闭右闭
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size()-1;

        while(left <= right) //left == right 有意义
        {
            int mid = left + (right-left)/2; // 防止溢出

            if(nums[mid] > target) 
            {
                right = mid-1;
            }
            else if(nums[mid] < target)
            {
                left = mid+1;
            }
            else
            {
                return mid;
            }
        }

        return -1;
    }
};

写法二:定义左闭右开区间。当我们定义左闭右开区间时,left == right 这种情况是没有意义的。

// 定义左闭右开
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size();

        while(left < right) //left == right 没有意义
        {
            int mid = left+(right-left)/2;
            if(nums[mid] < target)
            {
                left = mid+1;
            }
            else if(nums[mid] > target)
            {
                right = mid;
            }
            else
            {
                return mid;
            }
        }
        return -1;
    }
};

2.2双指针法

什么是双指针法?

双指针法就是用两个指针来标记数组中的元素,能够提高遍历的效率。

我们平时遍历数组的时候,都是使用一个变量来标记数组遍历的位置,但是有些情况下,一个变量不够用,这个时候就可以考虑多使用一个变量;如果还是不够,你甚至还能再增加一个变量来标记。

例题

题目链接 —— 移除元素  (题目来源于力扣)

题目:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。

题目分析

题目说要我们原地移除所有数值等于val元素,但是数组是连续的空间,如果在中间移除元素,就需要将后面所有的元素都向前移动一个位置,这样一来时间复杂的会非常大。所以,在解决数组中需要删除元素的时候,我们通常考虑找一个合适的值,使用覆盖来达到删除的目的。

解题代码

解法一:暴力解法

// 暴力解法
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size = nums.size();
        for(int i = 0; i < size; ++i) // 遍历数组,找到需要删除的值
        {
            if(nums[i] == val) //就需要移除了
            {
                // 从需要删除的值的后面找一个值来覆盖
                for(int j = i+1; j < size; ++j) 
                {
                    nums[j-1] = nums[j];
                }
                i--;
                size--;
            }
        }

        return size;
    }
};

解法二:双指针法

// 双指针解法
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int fast = 0;
        int slow = 0;
        for(fast = 0; fast < nums.size(); ++fast)
        {
            if(nums[fast] != val)  // 找到不等于val的值放到前面去
            {
                nums[slow++] = nums[fast];
            }
        }

        return slow;
    }
};

双指针法对比与暴力解法,可以看出省略了一个查找替换循环的循环,减少了不必要的重复的查找。因此优化了代码的时间复杂度。

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

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

相关文章

C++,STL 047(24.10.24)

内容 对set容器的元素进行查找与统计。 运行代码 #include <iostream> #include <set>using namespace std;void printSet(set<int> &s) {for (set<int>::iterator it s.begin(); it ! s.end(); it){cout << *it << " ";…

linux-牛刀小试

题目一&#xff1a; 1.第一问 首先创建用户tab在超级用户root的终端输入useradd tab 切换到tab用户&#xff1a; 推出重新登录到tab用户或者su – tab切换到tab用户 2.第二问 在桌面创建SHEGNCHAN目录 在SHENGCHAN文件夹下创建相应的文件&#xff1a; 3.第三问 首先&#…

哈希表【闭散列/开散列】

&#x1f33b;个人主页&#xff1a;路飞雪吖~ &#x1f320;专栏&#xff1a;C/C 目录 一、unordered系列关联式容器 &#x1f31f;unordered_map ⭐unordered_map接口说明 二、底层结构 &#x1f31f;哈希概念 &#x1f31f;哈希冲突 &#x1f31f;哈希函数 &#x1f3…

基于Leaflet和SpringBoot的全球国家综合检索WebGIS可视化

目录 前言 一、Java后台程序设计 1、业务层设计 2、控制层设计 二、WebGIS可视化实现 1、侧边栏展示 2、空间边界信息展示 三、标注成果展示 1、面积最大的国家 2、国土面积最小的国家 3、海拔最低的国家 4、最大的群岛国家 四、总结 前言 在前面的博文中&#xff…

【随手笔记】远程升级之如何平衡下载包大小与速率?

1. 远程升级基本信息 使用NB_BC26模组&#xff0c;通过AT指令使用TCP的协议与公司后台交互升级的固件为BIN文件&#xff0c;使用原始固件包升级&#xff0c;未使用差分方式原始固件包有110K,大小左右&#xff0c;固件的存储为外置的FLASH W25Q16,w25q16最小存储单位为页&#…

AListFlutter(手机alist)——一键安装,可在手机/电视上运行并挂载各个网盘

前面提到软路由系统OpenWRT的时候&#xff0c;当时说过可以在OpenWRT里安装alist&#xff0c;然后挂载网盘&#xff0c;这样就可以通过webdav的方式在家庭局域网下的任何设备都可以访问操作这些网盘&#xff0c;摆脱硬盘空间不够的问题。 但alist的官方版本是没有手机版本的&a…

【Java】探秘正则表达式:深度解析与精妙运用

目录 引言 一、基本概念 1.1 元字符 1.2 预定义字符类 1.3 边界匹配符 1.4 数量标识符 1.5 捕获与非捕获分组 二、Java中的正则表达式支持 三、正则表达式的使用示例 3.1 匹配字符串 3.2 替换字符串 3.3 分割字符串 3.4 使用Pattern和Matcher 3.5 捕获组和后向…

我了个超绝MATLAB——基础

由于要参加美赛&#xff0c;不想拖对队友们的后腿&#xff0c;于是一怒之下……怒了一下 创建MATLAB脚本 创建脚本 在主页——新建——脚本 中新建脚本&#xff08;Ctrln&#xff09; 保存 编辑器——保存&#xff08;Ctrls&#xff09; 运行 编辑器——运行&#xff08;F5&…

iOS--利用UITableViewDataSourcePrefetching实现平滑如丝的无限滚动

前言&#xff1a; 相信大家在网络不好的时候使用列表分页的App会获得非常不好的体验&#xff0c;由于网络的问题&#xff0c;会有明显的卡顿&#xff0c;就像抖音等App&#xff0c;那么我们是否能使用一些手段来优化这个体验呢&#xff1f;这里可以用到UITableView中另一个协议…

【案例演示】图像描述大模型示例及概念解释

【案例演示】图像描述大模型示例及概念解释 一、案例演示模型描述期望模型使用方式以及适用范围模型功能演示 二、大模型开源平台概览模型库的定义大模型开源平台 一、案例演示 模型链接&#xff1a;https://modelscope.cn/models/iic/mplug_image-captioning_coco_base_zh 模…

使用 CDN 后 Apache 的日志记录客户真实 IP

经常搭建网站服务器的都知道&#xff0c;在给站点使用了 CDN 后 Web 应用的日志记录里就会只记录 CDN 节点 IP 了&#xff0c;这就没法看到真实客户请求 IP&#xff0c;对于日志分析、运维日常维护来说就有点儿麻烦了&#xff0c;今天明月结合在五洛云服务器上搭建的Apache环境…

短视频账号矩阵系统源码---独立saas技术部署

#短视频账号矩阵系统# #短视频矩阵源码# #短视频账号矩阵系统技术开发# 抖音seo账号矩阵系统&#xff0c;短视频矩阵系统源码&#xff0c; 短视频矩阵是一种常见的视频编码标准&#xff0c;通过多账号一键授权管理的方式&#xff0c;为运营人员打造功能强大及全面的“矩阵式“…

liunx线程互斥

临界资源和临界区 临界资源&#xff1a;多线程执行流共享的资源就叫临界资源。 临界区&#xff1a;每个线程中&#xff0c;访问临界区的代码&#xff0c;就叫临界区。 互斥&#xff1a;任何时候&#xff0c;互斥保证只有一个执行流进入临界区&#xff0c;访问临界资源&#…

华为eNSP:端口安全

一、什么是端口安全 端口安全是指保护计算机端口免受未经授权的访问、攻击或滥用的一种措施。计算机上的每个服务或应用程序都依靠特定的端口进行通信。端口安全的目的是限制对计算机端口的访问&#xff0c;确保只有经过授权的用户或服务可以使用这些端口。通过配置防火墙、访…

C/C++(六)多态

本文将介绍C的另一个基于继承的重要且复杂的机制&#xff0c;多态。 一、多态的概念 多态&#xff0c;就是多种形态&#xff0c;通俗来说就是不同的对象去完成某个行为&#xff0c;会产生不同的状态。 多态严格意义上分为静态多态与动态多态&#xff0c;我们平常说的多态一般…

VulkanTutorial(1·环境搭建,渲染流程简述)

介绍&#xff1a; 与OpenGL&#xff0c;WebGL和Direct3D等API&#xff08;(Application Programming Interface, 应用程序编程接口)&#xff09;相比&#xff0c;valkan更偏向于底层&#xff0c;有更多的GPU控制接口&#xff0c;因此它有更好的性能和更小的驱动开销&#xff0…

【Python数据可视化】利用Matplotlib绘制美丽图表!

【Python数据可视化】利用Matplotlib绘制美丽图表&#xff01; 数据可视化是数据分析过程中的重要步骤&#xff0c;它能直观地展示数据的趋势、分布和相关性&#xff0c;帮助我们做出明智的决策。在 Python 中&#xff0c;Matplotlib 是最常用的可视化库之一&#xff0c;它功能…

【论文+源码】基于spring boot的垃圾分类网站

创建一个基于Spring Boot的垃圾分类网站涉及多个步骤&#xff0c;包括环境搭建、项目创建、数据库设计、后端服务开发、前端页面设计等。下面我将引导您完成这个过程。 第一步&#xff1a;准备环境 确保您的开发环境中安装了以下工具&#xff1a; Java JDK 8 或更高版本Mav…

python装饰器的另类用法

在对pyverilog源码进行单步调试时&#xff0c;遇到一个很奇怪的现象&#xff0c;被装饰器装饰的方法t_LINECOMMENT没有主动调用&#xff0c;但装饰器TOKEN中的内嵌函数set_regex却被调用了。 ## lexer.pyfrom ply.lex import *class VerilogLexer(object):linecomment r"…

C++【string类的使用】(上)

文章目录 1. 为什么要学习string类2. 标准库的string类2.1 string的构造函数&#xff08;1&#xff09;无参构造&#xff08;重点&#xff09;&#xff08;2&#xff09;用字符串初始化&#xff08;重点&#xff09;&#xff08;3&#xff09;用字符串的前n个字符初始化(4)拷贝…