当前位置: 首页 > news >正文

LeetCode 热题 100-----4. 移动零

一、题目解析

题目要求
给定一个数组(C++中叫vector,Python中叫list):

  1. 把所有0移动到数组末尾
  2. 非零元素的相对顺序不能改变(例如输入[1,3,12]移动后仍是1→3→12的顺序)
  3. 原地修改:不能创建新数组,必须直接修改原数组

示例

  • 输入:[0,1,0,3,12]→ 输出:[1,3,12,0,0]
  • 输入:[0]→ 输出:[0]

二、必备前置知识

为什么原地修改重要?在编程中,处理数组或列表时,创建新数组会占用额外的内存空间。这就像你在家里整理衣柜时,如果买个新衣柜来存放衣服,虽然整洁了,但浪费了空间和钱。原地修改则直接在原数组上操作,节省内存,效率更高。

专业解释:原地修改的算法通常只使用常数级别的额外空间,这意味着空间复杂度为$O(1)$。无论数组有多大(比如有1000个元素或10000个元素),算法只使用几个固定变量(如指针),而不需要开辟新数组。这在大数据处理中非常关键,能避免内存溢出。

类比理解:想象你在整理书架上的书。原地修改就像直接在书架上移动书本,不需要买新书架;而创建新数组就像租个新书架来放书,整理完再搬回来,既麻烦又占地方。另一个例子:超市收银时,收银员(慢指针)直接在收银台处理商品,顾客(快指针)递送商品,整个过程高效有序。

核心思想:双指针法是实现原地修改的常用技巧。它用两个变量(指针)协作:

  • 慢指针(slow):标记目标位置,比如存放非零元素的地方。它移动得慢,只在需要时更新位置。
  • 快指针(fast):遍历整个数组,快速寻找符合条件的元素(如非零值)。它移动得快,扫描所有元素。

深入理解数据结构

  • 在C++中的vector:vector是动态数组,能自动调整大小(如添加元素时扩容)。使用引用传参(如void func(vector<int>& nums))时,函数内修改nums会影响外部原数组,因为它传递的是原数组的引用,避免了值拷贝(复制整个数组)。代码示例:

    void modifyArray(vector<int>& nums) { // 加&表示引用传递 nums[0] = 100; // 直接修改原数组的第一个元素 }

    如果不加&,函数会创建副本,修改不影响原数组。

  • 在Python中的列表(list):列表是可变对象,函数内修改会直接影响原列表。类型注解(如def func(nums: List[int]) -> None)只是提示参数类型,不影响运行。代码示例:

    def modify_list(nums: list[int]) -> None: nums[0] = 100 # 直接修改原列表,无需返回新列表

    这里,nums是原列表的引用,修改后外部列表也会变。


三、核心解题思路(双指针法最优解)

一句话总结:双指针法通过慢指针标记位置、快指针遍历数组,实现原地移动元素,高效且节省内存。

分步拆解(以移动零问题为例)

  1. 初始化:设置慢指针slow = 0(指向数组起始位置,这里将存放第一个非零元素)。
  2. 快指针遍历:快指针fast从左到右扫描数组:
    • 遇到非零元素:将当前元素复制到slow位置,然后slow右移(slow += 1)。这就像收银员接收商品并放到正确位置。
    • 遇到零元素:跳过,不处理。fast继续移动,但slow不动。
  3. 补零操作:遍历结束后,从slow位置开始到数组末尾,所有元素赋值为0。确保零元素被移到末尾。

为什么这个方法高效?它只在必要时复制元素(非零值),避免了多次移动或创建临时数组。空间复杂度为$O(1)$,时间复杂度为$O(n)$(n是数组长度),非常高效。

示例演示(数组[0,1,0,3,12])

  • 步骤1fast在位置0,元素是0(零),跳过。slow保持在0。数组状态:[0,1,0,3,12]
  • 步骤2fast移动到位置1,元素是1(非零),复制到slow位置(0),slow右移到1。数组状态:[1,1,0,3,12](注意:原位置1的值被覆盖,但快指针会继续扫描)。
  • 步骤3fast移动到位置2,元素是0(零),跳过。slow保持在1。数组状态:[1,1,0,3,12]
  • 步骤4fast移动到位置3,元素是3(非零),复制到slow位置(1),slow右移到2。数组状态:[1,3,0,3,12](位置1的值被更新为3)。
  • 步骤5fast移动到位置4,元素是12(非零),复制到slow位置(2),slow右移到3。数组状态:[1,3,12,3,12](位置2的值被更新为12)。
  • 步骤6:遍历结束,从slow位置(3)开始到末尾,赋值为0。最终数组:[1,3,12,0,0]

示例演示

步骤fast位置当前元素操作slow位置数组状态
100跳过0[0,1,0,3,12]
211赋值1[1,1,0,3,12]
320跳过1[1,1,0,3,12]
433赋值2[1,3,0,3,12]
5412赋值3[1,3,12,3,12]
6--补零-[1,3,12,0,0]
四、完整可执行代码(含主函数测试)
1. C++ 代码(逐行注释)
#include <iostream> #include <vector> using namespace std; class Solution { public: void moveZeroes(vector<int>& nums) { // 慢指针:标记非零元素存放位置 int slow = 0; // 快指针遍历整个数组 for (int fast = 0; fast < nums.size(); fast++) { if (nums[fast] != 0) { // 发现非零元素 nums[slow] = nums[fast]; // 复制到慢指针位置 slow++; // 慢指针后移 } } // 将剩余位置补零(从slow到末尾) for (int i = slow; i < nums.size(); i++) { nums[i] = 0; } } }; // 主函数:支持用户自定义输入 int main() { vector<int> nums; cout << "请输入数组长度:"; int n; cin >> n; cout << "请输入数组元素(用空格分隔):"; for (int i = 0; i < n; i++) { int num; cin >> num; nums.push_back(num); } Solution().moveZeroes(nums); // 调用移动函数 cout << "移动后的结果:"; for (int num : nums) { cout << num << " "; } return 0; }

测试示例
输入:

请输入数组长度:5 请输入数组元素:0 1 0 3 12

输出:

移动后的结果:1 3 12 0 0
2. Python 代码(逐行注释)
from typing import List class Solution: def moveZeroes(self, nums: List[int]) -> None: # 慢指针:标记非零元素存放位置 slow = 0 # 快指针遍历整个列表 for fast in range(len(nums)): if nums[fast] != 0: # 发现非零元素 nums[slow] = nums[fast] # 复制到慢指针位置 slow += 1 # 慢指针后移 # 将剩余位置补零(从slow到末尾) for i in range(slow, len(nums)): nums[i] = 0 # 主函数:支持用户自定义输入 if __name__ == "__main__": nums = list(map(int, input("请输入数组元素(用空格分隔):").split())) Solution().moveZeroes(nums) # 调用移动函数 print("移动后的结果:", end="") for num in nums: print(num, end=" ")

测试示例
输入:

请输入数组元素:0 1 0 3 12

输出:

移动后的结果:1 3 12 0 0

五、边界情况测试
输入输出说明
[0][0]单个零
[1,0,2][1,2,0]零在中间
[1,2,3][1,2,3]无零元素
[][]空数组

六、算法复杂度(进阶知识)
  • 时间复杂度:$O(n)$
    • 两次独立遍历(快指针遍历 + 补零),总操作次数$2n$。
  • 空间复杂度:$O(1)$
    • 仅使用slowfast等常数变量,满足原地修改要求。

总结
  • 核心方法:双指针法(慢指针存非零,快指针找非零)。
  • 关键规则
    1. 非零元素顺序不变
    2. 原地修改(空间复杂度$O(1)$)
http://www.cnnetsun.cn/news/2005081.html

相关文章:

  • 《每个女孩都是生活家》
  • Qwen-Image-Edit-2509保姆级入门:手把手教你用AI智能编辑图片,小白也能变大神
  • 3分钟永久激活Windows和Office:KMS_VL_ALL_AIO智能脚本终极指南
  • HTML函数在旧版Windows跑得动吗_系统版本与硬件协同影响【指南】
  • Spring Cloud Eureka停更后,我们团队是如何平滑迁移到Nacos的(附完整配置对比)
  • WooCommerce 多联盟站点动态追加 Affiliate ID 教程
  • 适合放在简历上的开源项目与练手项目Idea清单
  • 技术债务:识别、度量与管理策略
  • RTX 4090跑Stable Diffusion终极配置:从显存分配到LoRA训练避坑全攻略
  • Go语言的sync.RWMutex中的策略性能优化
  • GitX智能版本控制助手:告别Git命令行,让版本控制更高效
  • 高效解密网易云音乐NCM格式:ncmdumpGUI专业实战指南
  • AGV机械臂功率链路设计实战:动态响应、功率密度与可靠性的平衡之道
  • 短信验证码接口被刷如何防范?
  • Harness 架构 与 LangChain、LangGraph 三者联动 的底层逻辑 。<Harness 学习圣经> 之二
  • mfc140.dll文件丢失损坏怎么办? 免费下载方法分享
  • STNN算法研究
  • 卷积改进与轻量化:自适应任意采样:AKConv(可改变核卷积)在 YOLOv11 中的实战,应对极度形变目标
  • 考虑极端天气线路脆弱性的配电网分布式电源配置优化模型【IEEE33节点】(Matlab代码实现)
  • Dify车载问答系统开发全链路拆解:从语音唤醒适配到车规级响应优化的7大关键决策点
  • CSS如何高效引入样式表_对比link标签与import指令的性能差异
  • STM32串口调试流程讲解
  • 告别死记硬背:用Python+SymPy从焦点和顶点自动生成三维旋转抛物面方程
  • mysql如何使用like进行模糊查询_mysql模糊匹配实例
  • Chrome-QRCode:浏览器中的智能二维码助手,让跨设备分享变得如此简单
  • 跨境创业必备TK跨境多账号运营系统怎么选?
  • python国际短信API示例代码怎么实现?Requests库调用短信接口演示
  • 企业网站设计|网站建设公司哪家好?2026十家网站制作公司深度盘点
  • 5G/6G毫米波设计必看:如何用信道建模参数(RMS时延扩展、K因子)指导你的系统设计?
  • 旧电视焕新颜:手把手教你用mstar-bin-tool解包康佳LED37R5200PDF固件,实现精简与root