每日一题——力扣27. 移除元素(举一反三)

题目链接:https://leetcode.cn/problems/remove-element/description/

 

菜鸡写法:

// 函数定义,移除数组nums中所有值为val的元素,并返回新的数组长度
int removeElement(int* nums, int numsSize, int val) {// 如果数组长度为0,直接返回0if (numsSize == 0) {return 0;}// 初始化两个指针,left指向数组的开始,right指向数组的末尾int left = 0, right = numsSize - 1;// 初始化一个变量来记录值为val的元素数量int num_of_val = 0;// 初始化一个临时变量用于交换元素int tmp;// 如果数组只有一个元素if (left == right) {// 如果这个元素的值等于val,则将数组指针置为NULL,并返回0if (*(nums + left) == val) {nums = NULL;return 0;} else {// 否则返回1,因为数组中没有需要移除的元素return 1;}}// 开始遍历数组,直到left指针超过right指针for (; left < right;) {// 如果left指向的元素等于val,且right指向的元素不等于valif (*(nums + left) == val && *(nums + right) != val) {// 增加值为val的元素计数num_of_val += 1;// 交换left和right指向的元素tmp = *(nums + left);*(nums + left) = *(nums + right);*(nums + right) = tmp;// 移动left和right指针left += 1;right -= 1;} else {// 如果left指向的元素不等于val,则移动left指针if (*(nums + left) != val) {left += 1;}// 如果right指向的元素等于val,则增加计数并移动right指针if (*(nums + right) == val) {num_of_val += 1;right -= 1;}}}// 如果left和right指针相遇,且指向的元素等于valif (left == right && *(nums + left) == val) {// 将该元素移到数组的末尾,并增加计数for (; left <= numsSize - num_of_val - 2; left++) {*(nums + left) = *(nums + left + 1);}num_of_val += 1;}// 返回新的数组长度,即原数组长度减去值为val的元素数量return numsSize - num_of_val;
}

这段代码实现了移除数组中指定值的功能,其核心思想是使用双指针技术,一个指针从数组头开始,另一个从数组尾开始,通过交换元素的方式将所有值为val的元素移到数组的末尾。下面是对这段代码的点评:

代码结构与逻辑

  • 初始化:代码首先检查数组长度是否为0,如果是,则直接返回0。然后初始化两个指针leftright,分别指向数组的开始和末尾。
  • 单元素处理:对于只有一个元素的数组,代码进行了特殊处理,如果该元素等于val,则将数组指针置为NULL并返回0,否则返回1。
  • 双指针遍历:代码使用双指针遍历数组,如果left指向的元素等于valright指向的元素不等于val,则交换这两个元素,并移动指针。如果left指向的元素不等于val,则只移动left指针;如果right指向的元素等于val,则只移动right指针。
  • 边界情况处理:当leftright指针相遇时,如果指向的元素等于val,则将该元素移到数组的末尾。
  • 返回结果:最后,代码返回新的数组长度,即原数组长度减去值为val的元素数量。

时间复杂度分析

  • 最坏情况:在最坏的情况下,即数组中所有的元素都等于val,此时需要遍历整个数组,因此时间复杂度为O(n),其中n是数组的长度。
  • 最好情况:在最好的情况下,即数组中没有元素等于val,此时只需要遍历一次数组,时间复杂度同样为O(n)。

空间复杂度分析

  • 额外空间:代码只使用了常数级别的额外空间,包括用于交换元素的临时变量tmp和用于计数的变量num_of_val,因此空间复杂度为O(1)。

改进建议

  • 指针移动逻辑:在双指针遍历的过程中,代码中的指针移动逻辑可以进一步优化,以减少不必要的判断和操作。例如,可以直接在交换元素的同时移动指针,而不需要额外的判断。
  • 边界处理:对于leftright指针相遇时的处理,可以考虑直接在遍历过程中处理,而不是在遍历结束后单独处理。
  • 返回数组指针:如果需要返回修改后的数组指针,可以考虑在函数中返回,而不是仅仅返回新的数组长度。

总体来说,这段代码实现了移除数组中指定值的功能,具有较好的时间复杂度和空间复杂度,但在代码逻辑和边界处理上还有一定的优化空间。
 

双指针优化法:
 

int removeElement(int* nums, int numsSize, int val) {// 初始化两个指针,`i` 用于遍历数组,`j` 用于记录新数组的长度int i = 0, j = 0;// 遍历整个数组for (i = 0; i < numsSize; i++) {// 如果当前元素不等于 `val`if (nums[i] != val) {// 将当前元素移动到新数组的位置 `j`nums[j] = nums[i];// 更新新数组的长度j++;}}// 返回新数组的长度return j;
}

逐行注释解释:

  1. int i = 0, j = 0;:初始化两个指针,i 用于遍历数组,j 用于记录新数组的长度。
  2. for (i = 0; i < numsSize; i++) {:开始遍历数组,i 从0开始,直到数组的长度。
  3. if (nums[i] != val) {:检查当前元素是否不等于 val
  4. nums[j] = nums[i];:如果当前元素不等于 val,将其移动到新数组的位置 j
  5. j++;:更新新数组的长度,即 j 指针向前移动一位。
  6. return j;:遍历结束后,返回新数组的长度。

时间复杂度和空间复杂度:

  • 时间复杂度:O(n),其中 n 是数组的长度。我们只需要遍历一次数组。
  • 空间复杂度:O(1),我们只使用了常数级别的额外空间。

改进建议:

这段代码已经很高效,没有明显的改进空间。它简洁、直观地实现了移除数组中指定值的功能,同时保持了较低的时间和空间复杂度。
 

该方法体现了以下哲学和编程思想:

  1. 简洁性(Simplicity):代码力求简洁明了,避免不必要的复杂性。通过使用两个指针,代码直接实现了移除指定值的功能,没有引入额外的数据结构或复杂的逻辑。

  2. 效率(Efficiency):双指针方法在时间复杂度上达到了O(n),这意味着它只需要遍历一次数组,是一种非常高效的处理方式。同时,空间复杂度为O(1),表明代码只使用了常数级别的额外空间,符合空间效率的要求。

  3. 迭代(Iteration):代码通过迭代的方式遍历数组,这是一种常见的编程模式,用于处理数组、列表等线性数据结构。迭代允许我们逐步处理数据,而不是一次性处理,这有助于提高代码的可读性和可维护性。

  4. 指针操作(Pointer Manipulation):在C语言中,指针是一种强大的工具,可以直接操作内存。双指针方法巧妙地利用了指针来跟踪数组中的元素,这是一种低级别的编程技巧,体现了对内存管理的深刻理解。

  5. 条件逻辑(Conditional Logic):代码中使用了条件语句(if)来决定是否将当前元素添加到新数组中。这种基于条件的逻辑是编程中的基本思想,用于根据不同的情况执行不同的操作。

  6. 增量式开发(Incremental Development):虽然代码本身很简洁,但在开发过程中,程序员可能会逐步添加功能,测试每个部分,确保代码的正确性。这种增量式的开发方法有助于减少错误,提高代码质量。

  7. 算法思维(Algorithmic Thinking):双指针方法是一种特定的算法,用于解决特定类型的问题。这种思维方式要求程序员能够识别问题的模式,并设计出有效的解决方案。

  8. 抽象(Abstraction):代码抽象了移除数组中指定值的逻辑,使得其他开发者可以不关心具体的实现细节,直接使用这个函数来完成任务。

举一反三

基于双指针方法的思想和应用,以下是一些技巧和原则,可以帮助你在其他编程任务中举一反三:

1. 空间优化

  • 原地操作:尽可能在输入的数据结构上直接进行修改,减少额外空间的使用。例如,在排序、合并或修改数组和链表时,考虑是否可以通过调整元素位置而不是创建新的数据结构来实现。

2. 时间优化

  • 单遍扫描:设计算法时,尽量确保只需要遍历数据一次。多数使用双指针的场景,如去除重复项、查找对应和等,都是基于只遍历一遍数据来实现的。

3. 思维模式

  • 对撞指针:在排序数组或链表中寻找两个数的和等特定问题时,可以从两端开始遍历,根据条件向中间逼近。
  • 快慢指针:用于解决循环链表的判断、查找中间节点等问题。快指针的移动速度是慢指针的两倍,通过它们的相对速度差来解决问题。

4. 抽象思维

  • 泛化问题解决方案:学习双指针技巧的同时,思考其背后的原理,例如减少不必要的遍历、空间优化等。这些原理不仅仅适用于数组和链表,也可以扩展到其他数据结构和问题上。

5. 条件逻辑与增量思想

  • 逐步构建解决方案:在解决问题时,从最简单的情况开始,逐渐添加条件和逻辑,以构建完整的解决方案。这有助于避免一开始就陷入复杂的情况,使问题更易于理解和解决。
  • 灵活应用条件判断:在使用双指针技巧时,根据问题的需求调整指针的移动条件。例如,满足某条件时移动左指针,不满足时移动右指针,或者两者都移动。

6. 代码优化与重构

  • 迭代与重构:在初步实现功能后,通过测试和验证来识别性能瓶颈或不必要的复杂度,然后迭代优化。这可能涉及调整算法、简化逻辑或利用更高效的数据结构。

通过掌握这些技巧和原则,你可以将双指针方法背后的思想应用到更宽泛的编程问题和数据结构上,提高代码的效率和质量。记住,好的编程实践不仅仅是关于解决一个特定问题,而是学会如何以一种可扩展、可维护的方式思考和实现解决方案。

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

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

相关文章

聚观早报 | 苹果新款iPad Pro发布;国产特斯拉4月交付量

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 5月9日消息 苹果新款iPad Pro发布 国产特斯拉4月交付量 iOS 18新功能爆料 真我GT Neo6续航细节 三星Galaxy Z F…

java图片水印字体乱码问题

问题描述&#xff1a;在linux Centos-7.5_64bit系统的其他服务器上不乱码&#xff0c;在部署项目的正式服务器乱码 水印字体设置是 微软雅黑 Font wordFont new Font("微软雅黑", Font.ITALIC,(srcImgHeightsrcImgWidth)/50); 一.Springboot项目&#xff0c;部署在…

文献速递:深度学习医学影像心脏疾病检测与诊断--基于迁移学习的生成对抗网络用于静态和动态心脏PET的衰减校正

Title 题目 Transfer learning‑based attenuation correction for static and dynamic cardiac PET using a generative adversarial network 基于迁移学习的生成对抗网络用于静态和动态心脏PET的衰减校正 01 文献速递介绍 心脏正电子发射断层扫描&#xff08;PET&#xf…

Redis 支持的 Java 客户端都有哪些?

Redis 是一种高性能的键值存储系统&#xff0c;它以其快速、灵活和可扩展的特性而闻名。在 Java 开发中&#xff0c;与 Redis 交互的方式通常是通过使用 Redis 的 Java 客户端。 这些客户端提供了访问 Redis 数据库的接口&#xff0c;使开发人员能够在 Java 应用程序中轻松地使…

伦敦金交易常识 原来可以这样分类

如果投资者想做好伦敦金交易&#xff0c;对市场中的伦敦金交易常识等等都需要加以学习和研究&#xff0c;别小看那些伦敦金交易常识&#xff0c;很多高深的交易策略也是从常识出发慢慢建立起来的。伦敦金交易常识可以分为几类&#xff0c;下面我们就来讨论一下。 伦敦金市场的基…

Git 如何管理标签命令(tag)

1.查看本地仓库tag --1.查看本地仓库tag UserDESKTOP-2NRT2ST MINGW64 /e/GITROOT/STARiBOSS/STARiBOSS-5GCA (gw_frontend_master) $ git tag 1stBossUpgrade V10.0.1_20220224_test V10.0.1_20220301_test tag-gwfrontend-V1.0.12-230625 tag-gw_frontend-23.08.29 tag-gw_f…

Docker快速搭建NAS服务——FileBrowser

Docker快速搭建NAS服务——FileBrowser 文章目录 前言FileBrowser的搭建docker-compose文件编写运行及访问 总结 前言 本文主要讲解如何使用docker在本地快速搭建NAS服务&#xff0c;这里主要写如下两种&#xff1a; FileBrowser1&#xff1a;是一个开源的Web文件管理器&…

45.乐理基础-音符的组合方式-复附点

复附点&#xff1a; 复附点顾名思义就是两个附点 复附点表示的音符&#xff0c;有多少拍&#xff1f;下面拿 复附点四分音符举例&#xff0c;可以把整个音符看成三部分&#xff0c;第一部分是原本的四分音符&#xff0c;第二部分是第一个附点&#xff0c;第三部分是第二个附点&…

java注解全网最细

引言 在java编程中&#xff0c;注解&#xff08;Annotation&#xff09;是一种元数据&#xff0c;它提供了关于程序代码的额外信息。注解不直接影响程序的执行&#xff0c;但可以在运行时提供有关程序的信息&#xff0c;或者让编译器执行额外的检查。 下面笔者通过循序渐进的…

【全开源】Java外卖霸王餐免费吃外卖小程序+APP+公众号+H5多端霸王餐源码

一、特色功能 霸王餐活动管理&#xff1a;允许商家发布和管理霸王餐活动&#xff0c;包括设置活动时间、具体优惠、活动规则等。用户参与与浏览&#xff1a;用户可以在小程序中浏览霸王餐活动列表&#xff0c;查看活动的详情信息&#xff0c;如商品或服务的免费赠送、活动规则…

用户体验优化uxo指的是什么?

用户体验优化(User Experience Optimization&#xff0c;简称UXO)是一种专注于改善和提升用户在使用企业产品或服务时的整体感受和体验的过程。简单来说&#xff0c;它旨在通过改进产品或服务的设计和功能&#xff0c;使用户在使用过程中感到更加愉悦、满意和高效。用户体验优化…

设计软件有哪些?渲染软件篇(3),渲染100邀请码1a12

今天我们继续介绍几款渲染软件&#xff0c;方便大家了解 1、渲染100(http://www.xuanran100.com/?ycode1a12) 渲染100是网渲平台&#xff0c;为设计师提供高性能的渲染服务。通过它设计师可以把本地渲染移到云端进行&#xff0c;速度快价格便宜&#xff0c;支持3dmax、vray、…