经典问题:移动零 | 是0就得往后挪~

简介: 这是一道LeetCode经典题目“移动零”。

前言


这是一道LeetCode经典题目“移动零”。


题目要求:


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


请注意 ,必须在不复制数组的情况下原地对数组进行操作。


示例:


输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]


方案一:splice与pop


通过分析题目可知,是要找到每一个0,然后将0移动到数组的末尾,同时要注意的是不复制数组,影响的是元素组。


Up Code ~ 上代码 ~


/**
 * @method moveZeroes
 * @description 移动零 - pop/splice
 * @param nums number[]
 * @returns void
 */
function moveZeroes1(nums: number[]): void {
  // 获取数组的长度
  const len = nums.length;
  // 临界点判定
  if (len === 0) return;
  // 用来记录已经挪动到后面的0元素的个数,尽量减少下面for循环执行的次数
  let zeroLength = 0;
  // 遍历每一个元素
  for (let i = 0; i < len - zeroLength; i++) {
    // 判断当前元素是否为0
    if (nums[i] === 0) {
      // 将数组当前i索引位置的元素给移除
      nums.splice(i, 1);
      // 这里一定要注意,如果splice移除了元素之后,在其后的所有元素都会往前走一步
      // 比如移除的是索引0位置,原先索引1位置跑到了索引0位置,不进行处理的话,这个位置的元素就会被跳过
      // 所以这里的i要进行--操作,重新再指向当前位置索引
      i--;
      // 数组末尾添加0
      nums.push(0);
      // 记录里被移动的0的元素个数
      zeroLength++;
    }
  }
}


功能测试:


const nums = [0, 1, 0, 3, 12];
moveZeroes1(nums);
console.log(nums); // [1, 3, 12, 0, 0]


需求是满足了,但是这个是理想的方案吗?!


在方案一中,使用splice来切割数组,这是非常不理智的行为,每移除一个元素,在其后的所有元素都要向前移动,带来了时间复杂度O(n)O(n)O(n)


方案一的整体时间复杂度为:O(n2)O(n^2)O(n2)


有没有更优的方案呢?


方案二:双指针


定义变量j,永远指向nums中第一个0,在遍历nums时,遇到非0元素,则与当前0指引的位置进行元素交换,这样0就移动到了后面,非0元素移动到了前面。然后将j++指向下一个0的位置。


Up Code ~ 上代码 ~


/**
 * @method moveZeroes2
 * @description 移动零 - 双指针
 * @param nums number[]
 * @returns void
 */
function moveZeroes2(nums: number[]): void {
  // 获取数组长度
  const len = nums.length;
  // 临界点判断
  if (len === 0) return;
  // !!!!重点 --- 永远指向第一个0
  // 初始值设置为 -1
  let j = -1;
  // 遍历nums的每一个元素
  for (i = 0; i < len; i++) {
    // 当遇到第1个0时,j还未赋值,进行初始赋值操作
    if (nums[i] === 0 && j < 0) {
      j = i;
    }
    // 遇到非0元素,同时j已经指向了第一个0
    if (nums[i] !== 0 && j >= 0) {
      // 数组结构,交换索引位置的值
      [nums[i], nums[j]] = [nums[j], nums[i]];
      // j索引向后移动一位,指向下一个0
      // 这个位置建议在纸上自己画一下指针移动的效果,就很容易明白了
      j++;
    }
  }
}


时间复杂度:只执行了一次for循环,时间复杂度为O(n)O(n)O(n)


功能测试:


const nums2 = [0, 1, 0, 3, 12];
moveZeroes2(nums2);
console.log(nums2); // [1, 3, 12, 0, 0]


性能比较


方案一的时间复杂度是O(n2)O(n^2)O(n2),方案二的时间复杂度是O(n)O(n)O(n),同时我们再加上实际效果测试比对下性能。


声明一个20W条数组的数组,包含很多0值和非0值


// 声明2个,方案2和方案1各用1个,避免影响
const nums1: number[] = [];
for (let i = 0; i < 20 * 20000; i++) {
  i % 10 === 0 ? nums1.push(0) : nums1.push(i);
}
const nums2: number[] = [];
for (let i = 0; i < 20 * 20000; i++) {
  i % 10 === 0 ? nums2.push(0) : nums2.push(i);
}
console.time('moveZeros - splice');
moveZeroes1(nums1);
console.timeEnd('moveZeros - splice');
console.time('moveZeros - doublePointer');
moveZeroes2(nums2);
console.timeEnd('moveZeros - doublePointer');


上图


网络异常,图片无法展示
|


我们可以看到,性能差距特别大,方案二的双指针算法明显优于方案一!


相关文章
|
7月前
|
机器学习/深度学习 数据采集 人工智能
几周速通大模型实习,你需要做什么?
这是一篇关于转行进入大模型AI应用开发领域的经验分享。作者凭借自身两年开发经验成功转型,并详细列出学习路线:从Python语言、框架(如LangChain、Flask、FastAPI)到NLP、LLM微调,涉及强化学习、数据清洗、RAG调优等技术。他还提到论文复现、量化模型的重要性,以及高学历和顶会论文对进入顶级公司(如九坤、幻方)的帮助。文中提及面试经历和技术挑战,强调技术深度与努力的必要性。最后,作者鼓励读者坚持学习,并计划全平台发布教程。
|
存储 算法
Leetcode第三题(无重复字符的最长子串)
这篇文章介绍了解决LeetCode第三题“无重复字符的最长子串”的算法,使用滑动窗口技术来找出给定字符串中最长的不含重复字符的子串,并提供了详细的代码实现和解释。
607 0
Leetcode第三题(无重复字符的最长子串)
|
9月前
|
机器学习/深度学习 数据采集 人工智能
CCF 乌鲁木齐会员沙龙(第10期)——CCF 会员与分部工委李贝主任讲座
2025年3月4日,CCF乌鲁木齐会员沙龙第10期在新疆大学举办。活动由CCF主办,汇聚专家、师生及行业代表逾400人。李贝主任以“人工智能时代的红利——CCF”为主题演讲,探讨AI技术发展及其对教育与边疆数字化建设的影响。钱育蓉教授致开幕辞,强调培养新一代计算机人才的重要性。互动环节热烈,活动在掌声中圆满落幕,推动学术与产业深度融合。阿里云支持AI时代高校人才培养和科研创新,提供免费算力资源。
|
存储 算法 数据处理
|
机器学习/深度学习 算法 PyTorch
深度学习笔记(十三):IOU、GIOU、DIOU、CIOU、EIOU、Focal EIOU、alpha IOU、SIOU、WIOU损失函数分析及Pytorch实现
这篇文章详细介绍了多种用于目标检测任务中的边界框回归损失函数,包括IOU、GIOU、DIOU、CIOU、EIOU、Focal EIOU、alpha IOU、SIOU和WIOU,并提供了它们的Pytorch实现代码。
2901 1
深度学习笔记(十三):IOU、GIOU、DIOU、CIOU、EIOU、Focal EIOU、alpha IOU、SIOU、WIOU损失函数分析及Pytorch实现
|
传感器 存储
基于STM32与FreeRTOS的四轴机械臂项目-1
基于STM32与FreeRTOS的四轴机械臂项目
基于STM32与FreeRTOS的四轴机械臂项目-1
|
安全 虚拟化 Docker
在win10中使用ModelScope官方镜像
为在办公环境笔记本win10上测试ModelScope的开源模型 ,记录踩坑过程
2579 0
在win10中使用ModelScope官方镜像
Python编程实战:如何将列表组装成一棵树结构
本文介绍了如何在Python中将列表转换为树结构。首先定义`TreeNode`类表示节点,包含值和子节点列表。然后,通过`list_to_tree`函数递归地将列表转为树。此外,还提供了添加和删除节点的方法。文章旨在帮助读者理解和操作树结构,以解决实际编程问题。
Python编程实战:如何将列表组装成一棵树结构
|
算法 Python
【Leetcode刷题Python】106.相交链表
采用双指针法来找出两个链表的相交起始节点,并详细解释了算法的时间和空间复杂度。
155 1
|
搜索推荐
想要刻录蓝光光盘吗? 快来了解最好的蓝光刻录软件!
在数字娱乐蓬勃发展的今天,追求高清震撼的视听体验已成为趋势。面对众多高清视频制作工具的选择难题,DVDFab Blu-ray Creator脱颖而出,被誉为最佳蓝光刻录软件。它不仅支持多种视频格式输入(如MP4, MKV)及高清1080p输出,还能制作个性化菜单,兼容不同输出介质(BD-R, BD-RE等)。只需几步即可完成从视频导入到成品输出的全过程,无论是家庭回忆还是专业项目都能完美呈现。