【LeetCode刷题】:双指针篇(移动零、复写零)

news2024/11/24 8:45:56

文章目录

      • 一、移动零
        • 1. 题目解析
        • 2. 算法原理
        • 3. 代码编写
      • 二、复写零
        • 1. 题目解析
        • 2. 算法原理
        • 3. 代码编写

一、移动零

在这里插入图片描述

1. 题目解析

题目:移动零【点击跳转题目】

大致题意就是将数组中所有为0的元素往后移,移到数组的末尾,但是所有的非零元素的顺序不能发生改变。例如:假设非零元素是1,2,3,4。那么移动完后的顺序依旧是1,2,3,4。

另外还有一个要求:那就是移动操作是要在原数组上进行操作,不能复制数组进行操作。

2. 算法原理

这种题目可以归为一类题:那就是 “数组划分/ 数组分块”,这类题就是给定一个数组,让我们按照某种要求或则特性对数组内的元素划分成几个区间。

对于这道题,就是将数组划分成两个区间,左边为非零元素,右边为零的元素。
在这里插入图片描述
解决这类题一般有一个经典的算法,那就是 《双指针算法》。这里的指针并非是真的指针,数组中我们通常使用下标来充当指针。
在这里插入图片描述
定义两个指针curdest
cur:从左往右扫描数组,遍历数组。
dest:已处理的区间内,非零元素的最后一个位置。

所以只要cur遍历完数组后,就说明待处理的区间已经结束,而dest已经将数组划分为两个部分,一部分是非零元素,另一个区间的元素全是零,及完成题目要求。
在这里插入图片描述
第一步:定义两个 “指针” cur和dest。
因为dest是非零元素的最后一个位置,而在扫描前是没有非零元素的,所以最开始dest初始化为 -1,cur是遍历数组,所以要从数组的第一个元素开始,即初始化为 0

第二步:cur遍历数组。cur遍历数组时会遇到两种情况:

  • 第一种就是遇到零,不做处理,让cur继续加一
  • 第二种就是cur为非零元素,因为dest是指向最后一个非零元素,所以先让dest加一,再交换cur和dest下标对应的值,这样dest所指向的就一定是一个非零元素。

一直重复这个过程,等cur遍历完数组后,得到的数组即为题目要求的数组。
在这里插入图片描述

3. 代码编写

C语言代码:

void moveZeroes(int* nums, int numsSize) {
    int cur = 0, dest = -1;  // 定义双指针
    while(cur < numsSize)
    {
        if(nums[cur] == 0)
            cur++;  // 遇到0,cur++
        else  // 遇到非0
        {
            ++dest;
            int tmp = nums[dest];
            nums[dest] = nums[cur];
            nums[cur] = tmp;
            cur++;
        }
    }
}

C++代码:

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        for(int cur = 0, dest = -1; cur < nums.size(); cur++)  // 定义双指针
        {
            if(nums[cur])  // 判断nums[cur]是否为0
                swap(nums[++dest], nums[cur]);  // 不为0先dest++, 在交换值
        }
    }
};

Python代码:

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        dest = -1  # 定义双指针
        for cur in range(0, len(nums)):
            if nums[cur]:  # 判断nums[cur]是否为0
                dest += 1  # 不为0,先让dest加1
                nums[dest], nums[cur] = nums[cur], nums[dest]  # 交换值

在这里插入图片描述

二、复写零

1. 题目解析

在这里插入图片描述
题目:复写零【点击跳转题目】

大致题意就是给定一个数组,将数组中为零的元素都复写一遍,复写后其他元素向右移。但是不能在超过数组长度的位置写入元素,切要在原数组上进行修改,不能拷贝数组进行操作。

例如:输入:arr = [1,0,2,3,0,4,5,0],输出:[1,0,0,2,3,0,0,4],解释:调用函数后,输入的数组将被修改为:[1,0,0,2,3,0,0,4]

2. 算法原理

这个题可以先根据 “异地” 操作,然后优化成双指针的 “就地” 操作。怎么进行 “异地” 操作呢?
我们可以先开辟一个和数组arr一样大的空数组,然后用两个指针curdestcur指向原数组arr的第一个元素,dest指向新开辟的数组的第一个位置,然后进行操作。当cur指向的值不为零时,将这个值拷贝给新数组,然后curdest都加一,当cur指向的值为零时,将零拷贝给新数组,然后dest加一,在写一个零,最后curdest都加一。一直重复这个操作,结束条件为dest大于或等于数组的大小。
在这里插入图片描述
可以看到,通过 “异地” 操作可以完成题意,接下来就是将 “异地” 操作优化为双指针的 “就地” 操作
我们可以先试试将curdest都指向原数组的第一个元素,然后模拟 “异地” 操作。
在这里插入图片描述
模拟到这就没必要模拟了,因为后面的结果都是错的,之后cur会一直是0,因为dest将原本数组里的值覆盖成了0。从前往后不行的话就可以试试从后往前进行模拟。从后往前模拟我们只需要提前知道最后一个要复写的数是哪个就行了。

按照示例一:最后一个要复写的数是4,那就只需要将cur指向4,dest指向数组的最后一个元素,然后从后往前进行复写操作,cur的值不为0,将dest的值修改成cur的值,curdest都想前移动一位。cur的值为0,将dest的值修改为cur的值后,dest向前移动一位,然后再将dest指向的值修改为0,最后curdest向前移动一位。知道cur小于0结束。
在这里插入图片描述
我们发现 “从后往前” 复写是可以完成题目要求的,现在最后的问题是如何找到最后一个要复写的数。要找到最后一个要复写的数,我们也可以运用双指针来找。定义curdest两个 “指针”cur指向数组的第一个位置,即初始化为0,dest指向第一个元素的前一个位置,即初始化为-1。然后当cur的值不为1时,dest向前前进一步,cur向前前进一步,当cur的值等于0时,cur前进一步,dest前进两步,当dest指向数组的最后一个元素时,cur指向的值就是最后一个要复写的元素。
在这里插入图片描述

注意:有些情况会导致dest越界,这种情况需要特殊处理。
在这里插入图片描述
因为要复写的最后一个数是0,所以只要数组越界,就只要将n - 1位置的数据修改成0(n为数组大小),然后cur减一,dest减二即可。
另外,在判断一个数是否是最后一个要复写的数的时候,一定要先判断dest是否是结束位置,不是结束位置cur才可以加一。
在这里插入图片描述

3. 代码编写

C语言代码:

void duplicateZeros(int* arr, int arrSize) {
    int cur = 0, dest = -1;  // 定义双指针
    while(cur < arrSize)
    {
        if(arr[cur])  dest++;  // 不等于0
        else dest += 2;  // 等于0
        if(dest >= arrSize - 1)  break;  // 判断dest是否到结束位置
        cur++;
    }
    // 处理边界情况
    if(dest == arrSize)
    {
        arr[arrSize - 1] = 0;
        cur--, dest -= 2;
    }
    // 从后往前进行复写
    while(cur >= 0)
    {
        if(arr[cur])  // arr[cur]不等于0
            arr[dest--] = arr[cur--];
        else  // arr[cur]等于0
        {
            arr[dest--] = 0;
            arr[dest--] = 0;
            cur--;
        }
    }
}

C++代码:

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int cur = 0, dest = -1, n = arr.size();
        // 找到最后一个复写的数
        while (cur < n) {
            if (arr[cur])
                dest++;
            else
                dest += 2;
            if (dest < n - 1)
                cur++;
            else
                break;
        }
        // 处理边界情况
        if (dest == n) {
            arr[n - 1] = 0;
            cur--, dest -= 2;
        }
        // 从后向前进行复写
        while (cur >= 0) {
            if (arr[cur] != 0) {
                arr[dest] = arr[cur];
                cur--, dest--;
            } else {
                arr[dest] = 0;
                arr[--dest] = 0;
                cur--, dest--;
            }
        }
    }
};

Python代码:

class Solution:
    def duplicateZeros(self, arr: List[int], cur = 0, dest = -1) -> None:
        """
        Do not return anything, modify arr in-place instead.
        """
        n = len(arr)
        # 找到最后一个要复写的数
        while cur < n:
            if arr[cur] != 0:
                dest += 1
            else:
                dest += 2
            if dest >= n - 1:
                break
            cur += 1

        # 处理边界情况
        if dest == n:
            arr[n - 1] = 0
            dest -= 2
            cur -= 1

        # 从后往前复写
        while cur >= 0:
            if arr[cur] != 0:
                arr[dest] = arr[cur]
                dest -= 1
                cur -= 1
            else:
                arr[dest] = 0
                dest -= 1
                arr[dest] = 0
                dest -= 1
                cur -= 1

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

shell脚本写代码

用简单的test语句来判断是否闰年 #! /bin/bash read -p "sd " yearif [ $((year%4)) -eq 0 -a $((year%100)) -ne 0 -o $((year%400)) -eq 0 ]thenecho "是润年"elseecho "不是闰年" fi判断一个数是否为偶数 #! /bin/bash read -p "…

PDF怎么转换成TXT文本?这4个方法简单还免费,pdf转txt就靠它!

PDF怎么转换成TXT文本&#xff1f;PDF文件虽然广泛支持&#xff0c;但在某些设备或软件上可能无法完全正确显示&#xff0c;尤其是当文件包含特殊字体或复杂布局时。此外&#xff0c;PDF文件的阅读体验也可能受到格式干扰&#xff0c;如复杂的背景颜色或字体样式。将PDF转换为T…

ABeam 德硕 | 在华外企ESG议题选择指南(1)—— 全球ESG发展趋势及行业环境

在华外企 ESG议题选择指南 系列文章&#xff08;1&#xff09; 引言 ESG议题*在全球投资领域迅速升温&#xff0c;根据全球可持续投资联盟&#xff08;GSIA&#xff09;[1]发布的《2022可持续投资报告》[2]&#xff0c;全球可持续投资规模高达30.3万亿美元&#xff0c;占总…

深度学习-图像处理篇-7MobileNet

MobileNetV1: 深度可分卷积操作 优势 MobileNetV2: MobileNetV3:

如何优化电源模块自动化测试的硬件设计?-纳米软件

电源模块在电子设备中起着至关重要的作用&#xff0c;其性能的好坏直接影响整个系统的稳定性和可靠性。随着科技的不断发展&#xff0c;对电源模块的性能要求越来越高&#xff0c;因此&#xff0c;电源模块自动化测试变得尤为重要。其中&#xff0c;硬件设计是实现高效、准确自…

OmniCorpus数据集:最大(百亿级别)多模态数据集

2024-06-12 &#xff0c;由上海人工智能实验室、哈尔滨工业大学、南京大学、复旦大学等联合创建OmniCorpus&#xff0c;一个达到百亿级别的图文交错数据集。它不仅规模空前&#xff0c;更以其多元化的数据来源和高质量的数据内容&#xff0c;为多模态大语言模型的研究提供了坚实…

揭秘Xinstall:如何实现H5页面与App间的无缝链接跳转?

在移动互联网时代&#xff0c;用户在不同应用间的切换已成为常态&#xff0c;而如何高效、便捷地引导用户从网页跳转到App指定页面&#xff0c;成为了众多开发者关注的焦点。今天&#xff0c;我们就来聊聊一种名为“深度链接&#xff08;Deep Linking&#xff09;”的技术&…

hznu.dodo C++ 实验A 文件

1.【描述】 输入10个整数存入文本文件example.txt中&#xff0c;文件每行存放5个整数&#xff0c;每行整数之间用一个空格间隔。行末不能有多余的空格。 【输入】 输入10个整数。 【输出】 生成文件example.txt&#xff0c;里面存放输入的10个整数。 不需要在屏幕上显示整数。 …

FineReport批量处理列宽

1、选定多列 2、右击 3、设置列宽

Library介绍(四)

标准单元描述 标准单元主要由以下几个部分构成&#xff0c;分别是引脚电容、power、timing组成。其中引脚电容主要包含input/output pin的电容值。 power主要包含每个pin的leakage power和internal power。 timing主要包括cell的input pin到output pin的rise delay和fall del…

变换器(Transformer)在医学成像中的应用(上)

在自然语言任务上取得前所未有的成功之后&#xff0c;Transformer已被成功应用于多个计算机视觉问题&#xff0c;取得了最先进的结果&#xff0c;并促使研究人员重新考虑卷积神经网络(CNNs)作为事实上标准操作符的优势地位。利用计算机视觉领域的这些进展&#xff0c;医学影像领…

异业联盟,新名词 助力企业生态共存体!

在当今这个快速变化的市场环境中&#xff0c;单一企业的力量往往难以独自应对激烈的竞争和不断变化的消费者需求。 异业联盟模式应运而生&#xff0c;它像一座桥梁&#xff0c;连接起不同行业、不同领域的商家或企业&#xff0c;通过资源共享、优势互补和互利共赢的合作方式&am…

Xinstall带你解锁App下载归因新姿势,轻松搞定推广难题

在移动互联网时代&#xff0c;App的推广和运营对于产品的成功至关重要。然而&#xff0c;推广者在App推广过程中面临着诸多痛点&#xff0c;其中最关键的问题之一就是如何准确追踪和分析App的下载归因。这时候&#xff0c;Xinstall作为专业的App下载归因工具&#xff0c;成为了…

介绍一下SAP 函数 NUMBER_GET_NEXT的妙用——获取SAP编码OBJECT

NUMBER_GET_NEXT 是 SAP 中用于获取下一个可用编号的函数模块&#xff0c;通常用于生成唯一的编号或序列号。这个函数模块的妙用在于它能够确保编号的唯一性和连续性&#xff0c;适用于需要生成订单号、发票号或其他业务对象编号的场景。 我在写ABAP程序时经常要调用这个函数来…

对序列化反序列化在项目中的使用优化

文章目录 序列化是什么&#xff1f;常见的序列化协议使用序列化反序列化序列化List反序列化List 查看源码&#xff0c;分析不足进行改善 序列化是什么&#xff1f; 如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中&#xff0c;或者在网络传输 Java 对象&#xff0c…

Unity3d动画插件DoTween使用指南

1、DoTween是什么&#xff1f; DoTween是一款对象动画类插件&#xff0c;它是一款针对Unity 3D编辑器的、快速高效的、安全的、面向对象的补间动画引擎&#xff0c;并且对C#语言开发做出了很多的优化。另外&#xff0c;它使得开发者无需通过Unity内置的Animator或Coroutines即可…

vue3 对 vue2 有什么优势

1、diff算法的优化--静态标记&#xff08;PatchFlag&#xff09; vue2中的虚拟dom是全量的对比&#xff08;每个节点不论写死的还是动态的都会一层一层比较&#xff0c;这就浪费了大部分事件在对比静态节点上&#xff09; vue3编译模板时&#xff0c;动态节点做标记 标记分为不…

给新手学ComfyUI的建议,以及几个免费工作流的分享!

前言 这是我学ComfyUI的经历&#xff0c;分享给你们&#xff0c;也许你们可以少走一些弯路。 给新手建议 刚开始学ComfyUI的时候&#xff0c;是想做AI写真的&#xff0c;但是SD一次AI写真都没做过&#xff0c;所以相当于SD零基础。然后我就去哔哩哔哩找教程看&#xff0c;跟…

日常记账:解锁生活财务管理的秘密钥匙

在日常生活的纷繁复杂中&#xff0c;我们往往容易忽视那些细微却重要的财务流动。每一笔支出&#xff0c;无论大小&#xff0c;都是生活乐章中的一个音符。而日常记账&#xff0c;就是那把能够解锁生活财务管理秘密的钥匙。它不仅仅是一种简单的记录行为&#xff0c;更是一种对…

【大学学习-大学之路-回顾-电子计算机相关专业-学习方案-自我学习-大一新生(1)】

【大学学习-大学之路-回顾-电子&计算机相关专业-学习方案-自我学习-大一新生&#xff08;1&#xff09;】 1-前言2-整体说明&#xff08;1&#xff09;打字训练&#xff08;1&#xff09;字母区分大小写&#xff1a;&#xff08;2&#xff09;自动换行&不自动换行&…