算法每日双题精讲——双指针(移动零,复写零)

🌟快来参与讨论💬,点赞👍、收藏⭐、分享📤,共创活力社区。 🌟

别再犹豫了!快来订阅我们的算法每日双题精讲专栏,一起踏上算法学习的精彩之旅吧!💪


💯前言

在算法的世界里,双指针技巧常常能发挥出神奇的作用😎。今天,我们就来精讲两道利用双指针解决的经典题目:移动零和复写零。 

📣由于俩道题目均为数组,这里的双指针算法指的是:利用数组下标代替指针

当我们遇到,数组分块,数组划分的问题时,可以考虑使用双指针法。 

 


 💯双指针的作用

✍两个指针的作用:

  • cur:从左往右扫描数组,遍历数组
  • dest:已处理的区间内,非零元素的最后一个位置

分为三个区间:

  1. [0, dest]
  2. [dest + 1,cur -1]
  3. [cur, n -1]

💯移动零

题目链接👉【力扣】 

题目描述:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:
输入:nums = {0,1,0,3,12}
输出:{1,3,12,0,0}

⭐解题思路:

我们可以使用双指针法来解决这个问题。一个指针 cur用于遍历整个数组,另一个指针dest 用于指向当前非零元素应该放置的位置。当遇到非零元素时,将其放置在dest 指针所指的位置,并将dest 指针向后移动一位。遍历结束后,从dest 指针开始到数组末尾的位置全部设置为零。

 😀俩个指针将数组分为三个区间:

  1. [0, dest]:全是非0的元素(已经处理)
  2. [dest + 1,cur -1]:都是0(已经处理)
  3. [cur, n -1]:还未处理过的

 

 代码实现(以 C++ 为例):
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        // dest 用于标记已处理的非零元素的最后位置
        int dest = -1;
        // cur 用于遍历整个向量
        int cur = 0;
        while (cur < nums.size()) {
            // 如果当前位置的元素为 0
            if (nums[cur] == 0) {
                cur++;
            } else {
                // 先将 dest 加 1,标记下一个非零元素应放置的位置
                swap(nums[++dest], nums[cur]);
                cur++;
            }
        }
    }
};
👀复杂度分析:
  • 时间复杂度:O(n),其中 n 是数组的长度。我们只需要遍历一次数组。
  • 空间复杂度:O(1),只使用了有限的额外空间。

💯复写零

题目链接👉【力扣】

题目描述:给你一个长度固定的整数数组 arr,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。注意❗:不要在超过该数组长度的位置写入元素。

示例:
输入:arr = {1,0,2,3,0,4,5,0}
输出:{1,0,0,2,3,0,0,4}

 

解题思路:

同样可以使用双指针法来解决这个问题。一个指针cur用于遍历数组,另一个指针dest用于指向复写零后数组中元素应该放置的位置。当遇到零元素时,将dest指针后的元素依次向后移动两位,并在dest和 dest+1 的位置都放置零。当遇到非零元素时,将其放置在dest指针所指的位置,并将dest指针向后移动一位。

🙋这个解题思路是怎么来的呢? 
  1. 首先我们从左往右遍历数组, 

     

    当arr[cur]!=0时,我们让dest的后面一个的值赋予a[cur]正指向的那个值

    当arr[cur]==0时,我们让dest的后俩个值都赋予0

    当走到这一步时:


    cur找不到下一个为2的值了,因此我们不能从左往右遍
  2. 我们从右往左遍历,dset指向最后一个元素,cur指向最后一个要复写的数
    当a[cur]!=0时,让a[dest]=a[cur],然后cur--,dest--;
    当a[cur]==0时,让a[dest--]=0,a[dest--]=0,cur--;
    这样遍历没有问题,因此我们选择从右往左遍历
    所以我们只要找到最后要“复写”的数即可

⭐找最后一个要复写的数 

 

👇起初让cur指向数组的开头,dest指向-1的位置: 

代码实现(以 C++ 为例): 
class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        // dest 用于标记复制零元素后的新位置,初始值为 -1
        int dest = -1;
        // cur 用于遍历原始数组,初始值为 0
        int cur = 0;
        int n = arr.size();
        // 遍历原始数组,确定复制零元素后的新位置
        while (cur < n) {
            // 如果当前元素不为 0
            if (arr[cur]!= 0) {
                // dest 向后移动一位
                dest++;
            } else {
                // 如果当前元素为 0,dest 向后移动两位(因为要复制一个零)
                dest += 2;
            }
            // 如果 dest 已经到达或超过新数组的最后一个位置,跳出循环
            if (dest >= n - 1) break;
            // cur 向后移动一位,继续遍历原始数组
            cur++;
        }
        // 如果 dest 正好等于新数组的长度
        if (dest == n) {
            // 将新数组的最后一个位置设为 0
            arr[n - 1] = 0;
            // dest 回退两位
            dest -= 2;
            // cur 回退一位,因为上一步 cur 多走了一步
            cur--;
        }
        // 从后往前遍历原始数组,进行复制操作
        while (cur >= 0) {
            // 如果当前元素不为 0
            if (arr[cur]!= 0) {
                // 将当前元素复制到新位置
                arr[dest] = arr[cur];
                // cur 和 dest 都向前移动一位
                cur--;
                dest--;
            } else {
                // 如果当前元素为 0,先将 0 复制到 dest 位置,再将另一个 0 复制到 dest - 1 位置
                arr[dest--] = 0;
                arr[dest--] = 0;
                // cur 向前移动一位
                cur--;
            }
        }
    }
};
👀复杂度分析:
  • 时间复杂度:O(n),其中 n 是数组的长度。我们需要遍历两次数组。
  • 空间复杂度:O(1),只使用了有限的额外空间。

💯总结

通过这两道题目,我们可以看到双指针算法在处理数组相关问题时的高效性和灵活性👏。希望大家在今后的算法学习中,能够熟练掌握双指针技巧,解决更多复杂的问题💪。


我以后还会对 算法 相关知识进行更多的创作,欢迎大家关注我,一起探索 算法 的奇妙世界😜

👉【A Charmer】

 

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

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

相关文章

【SQL】在 SQL Server 中创建数据源是 MySQL 数据表的视图

背景&#xff1a;Windows系统已安装了mysql5.7和sqlServer数据库&#xff0c;现在需要在sqlServer创建视图或者查询来自mysql的数据&#xff0c;视图的数据来源mysql数据库。下面进行实现在sqlserver实现获取mysql数据表数据构建视图。 1、打开 ODBC 数据源管理器&#xff0c;…

Git 入门篇(一)

前言 操作系统&#xff1a;win11 64位 与gitee搭配使用 Git 入门篇&#xff08;一&#xff09; Git 入门篇&#xff08;二&#xff09; Git 入门篇&#xff08;三&#xff09; 目录 git下载、安装与配置 下载 安装 配置 git下载、安装与配置 下载 官网&#xff1a;git-…

【React】条件渲染——逻辑与运算符

条件渲染——逻辑与&&运算符 你会遇到的另一个常见的快捷表达式是 JavaScript 逻辑与&#xff08;&&&#xff09;运算符。在 React 组件里&#xff0c;通常用在当条件成立时&#xff0c;你想渲染一些 JSX&#xff0c;或者不做任何渲染。 function Item({ nam…

Ubuntu 的 ROS 操作系统安装与测试

引言 机器人操作系统&#xff08;ROS, Robot Operating System&#xff09;是一个用于开发机器人应用的开源框架&#xff0c;它提供了一系列功能丰富的库和工具&#xff0c;能够帮助开发者构建和控制机器人。 当前&#xff0c;ROS1的最新版本为Noetic Ninjemys&#xff0c;专为…

无人驾驶汽车——AI技术在交通领域的进展与未来展望

文章目录 前言什么是无人驾驶汽车?特斯拉的无人驾驶愿景无人驾驶的技术进程:从DARPA挑战赛到特斯拉中国无人驾驶技术的现状谷歌的加入与优步的竞争深度学习的到来与特斯拉的独特优势无人驾驶的未来:机遇与挑战总结前言 今天,我想通过讲一个故事,帮助大家更好地理解无人驾…

MCU面试题

面试题 1、Crotex-M 处理器才用的架构是"v7" Cortex-M3处理器是基于ARMv7-M架构的处理器&#xff0c;支持更丰富的指令集&#xff0c;包括许多32位指令&#xff0c;这些指令可以高效的使用高位寄存器。另外&#xff0c;M3还支持&#xff1a; 查表跳转指令和条件执行&…

基于多设计模式下的同步异步日志系统

目录 一、项目介绍 1.1 项目功能 1.2 开发环境 1.3 核心技术 1.4 环境搭建 二、日志系统介绍 2.1 日志系统存在的必要性 2.2 日志系统的技术实现 三、相关技术知识的补充 3.1 不定参函数的使用 3.2 设计模式 四、日志系统框架设计 4.1 模块划分 4.2 模块关系图 …

C# 选择导入文件的路径、导出文件的路径

通过C#代码&#xff0c;调出windows风格的文件选择对话框和存储文件对话框。提供界面来选择文件的位置&#xff0c;并将完整路径以字符串形式返回。 1、选择导入文件&#xff0c;获取其路径 C#通过这段代码将弹出一个文件选择对话框&#xff0c;允许用户选择一个文件&#xff…

【Hadoop实训】Hive 数据操作①

目录 一、准备文件 1、创建表 2、 数据映射 二、HIVE的数据操作 1、基本查询 a、全表查询 b、选择特定字段查询 c、查询员工表总人数 d、查询员工表总工资额 e、查询5条员工表的信息 2、Where条件查询 a、查询工资等于5000的所有员工 b、查询工资在500到1000的员工信息 …

Kylin Server V10 下自动安装并配置Kafka

Kafka是一个分布式的、分区的、多副本的消息发布-订阅系统&#xff0c;它提供了类似于JMS的特性&#xff0c;但在设计上完全不同&#xff0c;它具有消息持久化、高吞吐、分布式、多客户端支持、实时等特性&#xff0c;适用于离线和在线的消息消费&#xff0c;如常规的消息收集、…

goroutine 介绍

引子&#xff1a; 线程比如打开腾讯视频然后开始下载多个视频&#xff0c;下载任务就是线程 但是这并不是同时进行的&#xff0c;只是时间片比较短切换的比较快 进程和线程的关系 有些程序可以多进程有些可能不支持 并发和并行 并发和并行的根本区别是&#xff1a;并发在同一时…

AlphaProof IMO 2024 P1 in LEAN 之 简介

AlphaProof 是用于进行数学证明的人工智能&#xff0c;其中&#xff0c;对于 IMO 2024 中的6道题中的 4 道。本系列博文&#xff0c;就 AlphaProof 对于 IMO 2024 P1 给出的答案进行详细讲述。这里是此系列的第一篇。 IMO 2024 P1 题目如下&#xff1a; IMO 2024 P1 答案 α 为…

Android Framework AMS(11)广播组件分析-2(注册/注销流程解读)

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要解读广播组件的动态注册/注销过程及静态注册过程。关注思维导图中左上侧部分即可。 有了前面startActivity流程和service组件启动流程分…

Llamaindex RAG 实践

大模型支持的最强大的应用程序之一是复杂的问答聊天机器人。这些应用程序可以回答有关特定源信息的问题。这些应用程序使用一种称为检索增强生成 &#xff08;RAG&#xff09; 的技术。 1. 什么是RAG&#xff1f; 当你需要给模型注入新的知识时&#xff0c;有两种方法&#xf…

C#基础入门--类的建立与使用

上周刚开C#&#xff0c;这门课&#xff0c;第一节课就感觉不对劲了&#xff0c;感觉跟java很像(上图C#&#xff0c;下图java),进来页面都差不多&#xff1a; 这里介绍以下我C#的第一个程序&#xff0c;以类的思想定义一个student类&#xff0c;用户输入类中的属性信息后&#x…

LangChain Ollama实战文献检索助手(二)少样本提示FewShotPromptTemplate示例选择器

本期是用样例来提示大模型生成我们想要的答案。即在输入中给定提示的样例&#xff0c;以及提示模板&#xff0c;然后匹配较相关的样例进行文献综述。 创建示例样本FewShotPromptTemplate 这里我用GTP-o1生成了几个回答&#xff0c;作为样本 samples [{"theme": &…

GNN系统学习:简单图论、环境配置、PyG中图与图数据集的表示和使用

Reference datawhale开源学习资料 开篇 1.1 为什么要在图上进行深度学习&#xff1f; 在过去的深度学习应用中&#xff0c;我们接触的数据形式主要是这四种&#xff1a;矩阵、张量、序列&#xff08;sequence&#xff09;和时间序列&#xff08;time series&#xff09;。然…

嵌入式面试八股文(六)·ROM和RAM的区别、GPIO的八种工作模式、串行通讯和并行通讯的区别、同步串行和异步串行的区别

目录 1. ROM和RAM的区别 2. GPIO的八种工作模式 3. 串行通讯和并行通讯的区别 3.1 串行通讯 3.2 并行通讯 3.3 对比 4. 同步串行和异步串行的区别 4.1 时钟信号 4.2 数据传输效率 4.3 应用场景 4.4 硬件复杂性 1. ROM和RAM的区别 ROM&#xff08;Read-O…

批量缓存模版

批量缓存模版 缓存通常有两种使用方式&#xff0c;一种是Cache-Aside&#xff0c;一种是cache-through。也就是旁路缓存和缓存即数据源。 一般一种用于读&#xff0c;另一种用于读写。参考后台服务架构高性能设计之道。 最典型的Cache-Aside的样例&#xff1a; //读操作 da…

Vue3学习笔记(上)

Vue3学习笔记&#xff08;上&#xff09; Vue3的优势&#xff1a; 更容易维护&#xff1a; 组合式API更好的TypeScript支持 更快的速度&#xff1a; 重写diff算法模板编译优化更高效的组件初始化 更小的体积&#xff1a; 良好的TreeShaking按需引入 更优的数据响应式&#xf…