坚持用 清晰易懂的图解 + 多语言代码,让每道题变得简单!
呆头个人主页详情
呆头个人Gitee代码仓库
呆头详细专栏系列
座右铭: “不患无位,患所以立。”
在这里插入图片描述

面试必刷的数组三连:原地删除与合并

  • 前言
  • 目录
  • 1.移除元素
  • 2.删除有序数组中的重复项
      • 关键点
      • 详细步骤(以 `nums = [1, 1, 2, 2, 3]` 为例)
      • 为什么 `nums[i] == nums[k]` 时,`k` 不移动?
      • 总结
  • 3.合并两个有序数组
      • 解决思路
      • 解决代码
      • 代码解释
      • 示例
      • 复杂度分析


前言

🚀 你好,欢迎来到《编程闯关记》!
这里是算法与数据结构的实战基地,也是你从“暴力解法”到“最优解”的进化场。

🔍 专栏初衷

  • 清晰的图解 + 多语言代码(Python/Java/C++/C),拆解每道题背后的逻辑。
  • 不只讲“怎么做”,更讲“为什么”——从题目分析、边界条件到复杂度优化。
  • 适合想夯实基础突击面试的你,尤其针对LeetCode/牛客高频题!

💡 如何使用本专栏
1️⃣ 先独立思考:尝试自己写出第一版代码(哪怕很烂)。
2️⃣ 对比解法:看看我的思路和你的差异,吸收优化技巧。
3️⃣ 举一反三:每篇末尾会附相似题目链接,趁热打铁。

📌 坚持打卡
算法没有捷径,但正确的方法能让你少走弯路。每天15分钟,和我一起用代码雕刻思维!

(正文开始👇)


目录

1.移除元素

力扣链接直达<请点击
在这里插入图片描述
在这里插入图片描述

int removeElement(int* nums, int numsSize, int val) 
{int count = 0;for(int i=0;i<numsSize;i++){if(nums[i]!=val){nums[count] = nums[i];count++;}}return count;
}

代码说明:

  • 初始化计数count: count从0开始,用于记录不等于val的元素数量,并作为新数组的索引。

  • **遍历数组:**使用i遍历整个数组,检查每个元素是否等于val。

  • 如果nums[i]不等于val,则将其复制到nums[k]的位置,并递增k。

  • 如果等于val,则跳过该元素。

  • 返回结果:最终k即为新数组的长度,且数组的前k个元素均为不等于val的元素。

这种方法高效且满足题目要求的原地修改条件。
在这里插入图片描述

2.删除有序数组中的重复项

力扣链接直达<请点击
在这里插入图片描述
在这里插入图片描述

int removeDuplicates(int* nums, int numsSize) {if (numsSize == 0) return 0; // 空数组直接返回 0int k = 0; // 慢指针,初始指向第一个元素for (int i = 1; i < numsSize; i++) {if (nums[i] != nums[k]) { // 发现新元素k++; // 移动慢指针nums[k] = nums[i]; // 存入新元素}// 如果 nums[i] == nums[k],跳过(i 继续前进)}return k + 1; // 返回去重后的长度
}

在这里插入图片描述

“原地移除重复元素” 的算法中,当遇到重复元素时,并不是真的删除它,而是通过 覆盖(跳过) 的方式,让后面的非重复元素占据前面的位置,最终只保留不重复的部分。

关键点

  1. 不真正删除元素:数组在内存中是连续的,不能直接删除某个元素,只能覆盖。
  2. 双指针策略
    • 慢指针 k:记录 不重复元素应该存放的位置
    • 快指针 i:遍历数组,检查是否有新元素。
  3. 覆盖重复元素
    • 如果 nums[i] 是新元素(即 nums[i] != nums[k]),就把它存到 nums[k+1],然后 k++
    • 如果 nums[i] 是重复的(即 nums[i] == nums[k]),就 跳过它i 继续前进,k 不动)。

详细步骤(以 nums = [1, 1, 2, 2, 3] 为例)

iknums[i]nums[k]操作nums 变化
1011nums[i] == nums[k],跳过[1, 1, 2, 2, 3](不变)
2021nums[i] != nums[k],存入 nums[1] = 2k++[1, 2, 2, 2, 3]
3122nums[i] == nums[k],跳过[1, 2, 2, 2, 3](不变)
4132nums[i] != nums[k],存入 nums[2] = 3k++[1, 2, 3, 2, 3]

最终结果

  • k+1 = 3 个元素是去重后的:[1, 2, 3](后面的 2, 3 可以忽略)。
  • 返回 k + 1 = 3(去重后的长度)。

为什么 nums[i] == nums[k] 时,k 不移动?

  • k 指向的是当前不重复部分的最后一个元素
  • 如果 nums[i]nums[k] 相同,说明 nums[i] 是重复的,直接跳过(不存入数组)。
  • 只有遇到新元素时,才存入 nums[k+1] 并移动 k

总结

  • 移除重复元素 实际上是 用后面的不重复元素覆盖前面的重复位置
  • k 的作用
    • 记录 去重后的数组末尾位置
    • 只有遇到新元素时才移动 k,否则跳过。
  • 时间复杂度 O(n),空间复杂度 O(1)(原地修改)。

这样,最终 nums 的前 k+1 个元素就是去重后的结果,而后面部分可以忽略。

3.合并两个有序数组

力扣链接直达<请点击
在这里插入图片描述

解决思路

由于 nums1 有足够的空间容纳 nums2 的元素,我们可以利用 双指针 的方法从后向前合并,以避免覆盖 nums1 中还未处理的元素。具体步骤如下:

  1. 初始化指针

    • i 指向 nums1 的最后一个有效元素(即 m - 1)。
    • j 指向 nums2 的最后一个元素(即 n - 1)。
    • k 指向 nums1 的最后一个位置(即 m + n - 1)。
  2. 从后向前比较并合并

    • 比较 nums1[i]nums2[j],将较大的元素放到 nums1[k]
    • 移动相应的指针(ij)和 k
  3. 处理剩余元素

    • 如果 nums2 中还有剩余元素(即 j >= 0),直接复制到 nums1 的前面。

解决代码

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {int i = m - 1;      // nums1 的最后一个有效元素int j = n - 1;      // nums2 的最后一个元素int k = m + n - 1;  // nums1 的最后一个位置// 从后向前合并while (i >= 0 && j >= 0) {if (nums1[i] > nums2[j]) {nums1[k--] = nums1[i--];} else {nums1[k--] = nums2[j--];}}// 如果 nums2 还有剩余元素,直接复制到 nums1 的前面while (j >= 0) {nums1[k--] = nums2[j--];}
}

在这里插入图片描述

代码解释

  1. 初始化指针

    • inums1 的有效末尾开始。
    • jnums2 的末尾开始。
    • knums1 的最后一个位置开始。
  2. 比较并合并

    • 比较 nums1[i]nums2[j],将较大的元素放入 nums1[k],并移动相应的指针。
    • 这样确保 nums1 的后半部分不会被覆盖,直到所有元素正确归位。
  3. 处理剩余元素

    • 如果 nums2 中还有元素未合并(即 j >= 0),直接复制到 nums1 的前面,因为 nums1 的前面部分已经处理完毕。

示例

输入

nums1 = [1, 2, 3, 0, 0, 0], m = 3
nums2 = [2, 5, 6], n = 3

执行过程

  1. i = 2, j = 2, k = 5
    • nums1[2] = 3 < nums2[2] = 6nums1[5] = 6, j--, k--
  2. i = 2, j = 1, k = 4
    • nums1[2] = 3 < nums2[1] = 5nums1[4] = 5, j--, k--
  3. i = 2, j = 0, k = 3
    • nums1[2] = 3 > nums2[0] = 2nums1[3] = 3, i--, k--
  4. i = 1, j = 0, k = 2
    • nums1[1] = 2 == nums2[0] = 2nums1[2] = 2, j--, k--
  5. j = -1,循环结束。
  6. nums2 已无剩余元素,合并完成。

最终结果

nums1 = [1, 2, 2, 3, 5, 6]

复杂度分析

  • 时间复杂度:O(m + n),每个元素最多被比较一次。
  • 空间复杂度:O(1),没有使用额外空间,原地修改 nums1

这种方法高效且避免了不必要的元素移动,是最优解。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣经典算法篇-41-旋转图像(辅助数组法,原地旋转法)

1、题干 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a;输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]]…

译|用户增长策略如何使用因果机器学习的案例

来自上传文件中的文章《[Causal Machine Learning for Growth: Loyalty Programs, LTV, and What to Do When You Can’t Experiment | by Torty Sivill | Towards AI]》 本文探讨了当 A/B 测试不可行时&#xff0c;如何利用因果推断从历史数据中获取洞察。技术亮点在于通过构建…

java~final关键字

final关键字final基本介绍final的使用细节final基本介绍 final是最终的意思&#xff0c;可以修饰类&#xff0c;属性&#xff0c;方法&#xff0c;局部变量什么时候会要使用到final呢&#xff1f; 1.想要类不被继承时 2.不希望类的某个属性的值被改变时 3.不想父类的某个方法被…

Node.js(四)之数据库与身份认证

数据库与身份认证 目录 数据库与身份认证 十三、数据库的基本概念 13.1 什么是数据库 13.2 常见的数据库及分类 13.3 传统型数据库的数据组织结构 1. Excel 的数据组织结构 2. 传统型数据库的数据组织结构 3. 实际开发中库、表、行、字段的关系 十四、安装并配置MySQ…

SpringBoot+SpringMVC常用注解

文章目录发展历程项目创建项目结构入门案例配置文件的两种方式&#xff1a;只能使用一种创建项目二入门案例常用知识及注解Controller:类上面加&#xff0c;SpringMVC的注解GetMapping:方法上面加Spring框架的两项核心功能Component:组件。控制反转&#xff0c;加在业务类上面&…

标准GS相位恢复算法

标准GS相位恢复算法详解与MATLAB实现 Gerchberg-Saxton (GS) 算法是一种经典的相位恢复方法&#xff0c;广泛应用于光学成像、衍射成像和全息技术等领域。该算法通过迭代过程从未知相位的强度测量中恢复相位信息。 算法原理 GS算法的核心思想是利用傅里叶变换关系在空间域和频率…

【Linux网络编程基础--socket地址API】

一、主机字节序和网络字节序主机字节序&#xff08;Host Byte Order&#xff09;&#xff1a;你当前电脑的内存字节顺序&#xff08;比如 x86 是小端&#xff09;网络字节序&#xff08;Network Byte Order&#xff09;&#xff1a;统一规定为大端序&#xff08;高位字节在高位…

Linux路径MTU发现(Path MTU Discovery, PMTU)

Linux路径MTU发现&#xff08;Path MTU Discovery, PMTU&#xff09;机制是TCP/IP协议栈中确保数据包高效传输的核心技术。其核心目标是动态探测源主机到目的主机路径上的最小MTU&#xff08;Maximum Transmission Unit&#xff09;&#xff0c;从而避免IP分片&#xff0c;提升…

【MySQL进阶】------MySQL程序

MySQL程序简介 MySQL安装完成通常会包含如下程序&#xff1a; Linux系统程序⼀般在 /usr/bin⽬录下&#xff0c;可以通过命令查看&#xff1a; windows系统⽬录&#xff1a;你的安装路径\MySQL Server 8.0\bin&#xff0c;可以通过命令查看&#xff1a; 每个 MySQL 程序都有许…

Linux大页内存导致服务内存不足

Linux大页内存导致服务内存不足的解决方法 大页内存&#xff08;Huge Pages&#xff09;是Linux内核提供的一种机制&#xff0c;用于减少TLB&#xff08;转换后备缓冲区&#xff09;的压力&#xff0c;提高内存访问性能。然而&#xff0c;如果配置不当&#xff0c;大页内存可能…

超宽带测距+测角+无线通信一体化模组:智能门锁、智能遥控器、AR头戴、智能穿戴

超宽带测距测角无线通信一体化模组&#xff1a;智能门锁、智能遥控器、AR头戴、智能穿戴UWB测距测角技术&#xff0c;因其高精度、低延迟、抗干扰能力&#xff0c;正广泛应用于“人-物-设备”的空间感知场景&#xff0c;成为构建智能空间和精准互动的重要底层技术。代表厂商与产…

基于单片机空气质量检测/气体检测系统

传送门 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目速选一览表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目功能速览 概述 随着环境污染问题日益严重&#xff0c;空气质量监测成为社会关注的焦点。基于单片机的空气质量检…

网络安全 | 从 0 到 1 了解 WAF:Web 应用防火墙到底是什么?

&#x1f914; 写在前面 2020年 我参加公司的安全技能大赛&#xff0c;队友在实操环节启用了 WAF 防火墙&#xff0c;这是我第一次接触到 Web 应用防火墙。作为一个 Web 开发老鸟&#xff0c;真是羞愧呀&#x1f602;。 &#x1f510; Web应用防火墙 WAF 全称是 Web Applica…

服务器突然之间特别卡,什么原因?

原因总结&#xff1a;1.一般是本地网速的问题&#xff0c;服务器网速的问题&#xff0c;服务器CPU被占满的问题今天发现另一个会导致特别卡的问题&#xff0c;是主存占满也会导致卡顿。解释如下&#xff1a;当服务器的主存&#xff08;物理内存&#xff09;被完全占满时&#x…

AI应用标准详解:A2A MCP AG-UI

"OpenAI接入MCP&#xff0c;Google推出A2A&#xff0c;微软与OpenAI紧密绑定"标志着云计算竞争焦点已从"算力"和"模型参数"转向‌Agent标准协议控制权‌。在AI快速演进的今天&#xff0c;我们不再仅关注单个AI的智能水平&#xff0c;而是探索多个…

Web安全学习步骤

以下是Web安全专项学习步骤&#xff0c;聚焦实战能力培养&#xff0c;分为4个阶段资源清单**&#xff0c;适合从入门到进阶。重点培养漏洞挖掘能力与防御方案设计双重视角&#xff1a;---阶段1&#xff1a;Web技术筑基&#xff08;1-2个月&#xff09; | 领域 | 关键…

Android工程命令行打包并自动生成签名Apk

1.进入工程目录查看所有gradle任务 2.打包debug与release 打包前先生成jks签名文件test.jks 在工程的build.gradle中添加签名配置 signingConfigs {release {storeFile file("/home/dev/test.jks")storePassword "111111"keyAlias "key0"keyPas…

分布式微服务--Nacos作为配置中心(一)

1.Nacos配置远程配置中心注意总结&#xff1a;本地配置文件必须使用 bootstrap.yml 或 bootstrap.properties远程配置的加载优先于 application.yml&#xff0c;因此必须写在 bootstrap 配置文件中。本地配置文件中 file-extension 的取值仅支持两种&#xff1a;properties 或 …

Linux安装MySQL及链接第三方工具详细教程,带图带错误分析

本教程所有代码均为root用户权限下操作&#xff0c;如果不是root用户&#xff0c;在代码前加上&#xff08;sudo &#xff09;即可 一、安装MySQL服务 准备工作&#xff1a; 有时&#xff0c;系统无法解析 部分域名&#xff0c;导致无法获取镜像列表&#xff0c;从而无法安装…

WPS2024 软件下载及安装教程!

软件介绍 WPS Office是一套办公软件套装&#xff0c;包含WPS文字、WPS表格、WPS演示三大功能模块&#xff0c;可以满足常用文字处理、表格编辑和演示制作等多种办公需求&#xff0c;以其强大的功能和用户友好的界面赢得了众多用户的青睐。 软件&#xff1a;‌‌‌‌‌‌WPS Of…