面试高频算法专题:数组的双指针思想及应用(算法村第三关白银挑战)

news2024/11/26 15:01:19

所谓的双指针其实就是两个变量,不一定真的是指针。

  1. 快慢指针:一起向前走
  2. 对撞指针、相向指针:从两头向中间走
  3. 背向指针:从中间向两头走

移除值为val的元素

题目描述

27. 移除元素 - 力扣(LeetCode)

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例 2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,3,0,4]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

快慢指针

/**
 * @return 移除后val后数组的新长度
 */
public int removeElement(int[] nums, int val)
{
    int slow = 0;

    for (int fast = 0; fast < nums.length; fast++)
    {
        //nums[fast] == val时slow等待,直到下一个nums[fast](!= val),然后将nums[slow]覆盖
        if (nums[fast] != val)
        {
            nums[slow] = nums[fast];
            slow++; //slow移动到下一个待写位置
        }
    }

    return slow;
}

在这里插入图片描述

上图的val=2

对撞指针+交换

/**
 * 核心思想:从右侧找到不是val的值来顶替左侧是val的值
 */
public int removeElement(int[] nums, int val)
{
    int left = 0;
    int right = nums.length - 1;

    while (left <= right)
    {
        if(nums[left] == val && nums[right] != val)
        {
            int temp = nums[left];
            nums[left] = nums[right];   //覆盖删除
            nums[right] = temp; //交换的目的是让right指针能够移动
        }

        //更新指针
        if (nums[left] != val)
            left++;
        if (nums[right] == val)
            right--;
    }

    return left;
}

在这里插入图片描述

对撞指针+覆盖

public int removeElement(int[] nums, int val)
{
    int left = 0;
    int right = nums.length - 1;

    while (left <= right)
    {
        if (nums[left] == val)
        {
            nums[left] = nums[right];
            right--;
        }
        else
            left++;
    }

    return right + 1;
}

在这里插入图片描述

删除有序数组中的重复项

题目描述

26. 删除有序数组中的重复项 - 力扣(LeetCode)

给你一个 非严格递增排列 的数组 nums ,请你** 原地** 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k

示例 1:

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

示例 2:

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

提示:

  • 1 <= nums.length <= 3 * 104
  • -104 <= nums[i] <= 104
  • nums 已按 非严格递增 排列

快慢指针

public int removeDuplicates(int[] nums)
{
    int slow = 0;

    for(int fast = 0; fast < nums.length; fast++)
    {
        //fast往下走,直到指向第一个与slow不同的元素
        if(nums[fast] != nums[slow])
        {
            slow++; //slow后移一位,存储新的不重复元素
            nums[slow] = nums[fast];
        }
    }

    return slow + 1;    //返回nums中唯一元素的个数。
}

在这里插入图片描述

进阶:重复元素保留k个

  • k 位直接保留
  • **fast 不断向后遍历, nums[fast] 能够保留的前提是与nums[slow]**的前面第 k 个元素不同
  • 保留后 slow 指向新的写入位置
public int removeDuplicates(int[] nums)
{
    int slow = 0;
    int k = 2;  //重复元素保留k个

    for(int fast = 0; fast < nums.length; fast++)
    {
        if(slow < k  || nums[slow - k] != nums[fast])
        {
            nums[slow] = nums[fast];
            slow++;
        }
    }

    return slow;    //返回nums中唯一元素的个数。
}

按奇偶排序数组

题目描述

905. 按奇偶排序数组 - 力扣(LeetCode)

给你一个整数数组 nums,将 nums 中的的所有偶数元素移动到数组的前面,后跟所有奇数元素。

返回满足此条件的 任一数组 作为答案。

示例 1:

输入:nums = [3,1,2,4]
输出:[2,4,3,1]
解释:[4,2,3,1][2,4,1,3][4,2,1,3] 也会被视作正确答案。

示例 2:

输入:nums = [0]
输出:[0]

对撞指针+交换

public int[] sortArrayByParity(int[] nums)
{
    int left = 0;
    int right = nums.length - 1;

    while (left < right)
    {
        //左奇右偶时两边交换,把奇数调到后面,偶数调到前面
        if(nums[left] % 2 == 1  && nums[right] % 2 == 0)
        {
            int t = nums[left];
            nums[left] = nums[right];
            nums[right] = t;
        }

        if(nums[left] % 2 == 0) left++; //跳过左边的偶数
        if(nums[right] % 2 == 1) right--; //跳过右边的奇数
    }

    return nums;
}

数组轮转

题目描述

189. 轮转数组 - 力扣(LeetCode)

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1: [7,1,2,3,4,5,6]
向右轮转 2: [6,7,1,2,3,4,5]
向右轮转 3: [5,6,7,1,2,3,4]

示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释: 
向右轮转 1: [99,-1,-100,3]
向右轮转 2: [3,99,-1,-100]

提示:

  • 1 <= nums.length <= 105
  • -231 <= nums[i] <= 231 - 1
  • 0 <= k <= 105

进阶:

  • 尽可能想出更多的解决方案,至少有 三种 不同的方法可以解决这个问题。
  • 你可以使用空间复杂度为 O(1)原地 算法解决这个问题吗?

三次翻转

public void rotate(int[] nums, int k)
{
    //举例nums:[1,2,3,4,5,6,7]
    k = k % nums.length;
	
    reverse(nums,0, nums.length - 1);	//nums:[7,6,5,4,3,2,1]
    reverse(nums,0,k-1);	//nums:[5,6,7,4,3,2,1]
    reverse(nums,k, nums.length - 1);	//nums:[5,6,7,1,2,3,4]
}

//反转指定区间
public void reverse(int[] nums, int left, int right)
{
    while (left < right)
    {
        int t = nums[left];
        nums[left] = nums[right];
        nums[right] = t;

        left++;
        right--;
    }
}

数组的区间专题

汇总区间

题目描述

228. 汇总区间 - 力扣(LeetCode)

给定一个 无重复元素有序 整数数组 nums

返回 恰好覆盖数组中所有数字最小有序 区间范围列表* 。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x

列表中的每个区间范围 [a,b] 应该按如下格式输出:

  • "a->b" ,如果 a != b
  • "a" ,如果 a == b

示例 1:

输入:nums = [0,1,2,4,5,7]
输出:["0->2","4->5","7"]
解释:区间范围是:
[0,2] --> "0->2"
[4,5] --> "4->5"
[7,7] --> "7"

示例 2:

输入:nums = [0,2,3,4,6,8,9]
输出:["0","2->4","6","8->9"]
解释:区间范围是:
[0,0] --> "0"
[2,4] --> "2->4"
[6,6] --> "6"
[8,9] --> "8->9"

提示:

  • 0 <= nums.length <= 20
  • -231 <= nums[i] <= 231 - 1
  • nums 中的所有值都 互不相同
  • nums 按升序排列
  1. slow 指向每个区间的起始位置, fastslow 位置开始向后遍历,直到不满足连续递增或 fast 达到数组边界。当前区间结束
  2. slow 指向更新为 fast + 1,作为下一个区间的开始位置,fast继续向后遍历找下一个区间的结束位置,如此循环,直到 nums 遍历完毕

append(char c):将指定的字符添加到当前StringBuilder对象的末尾。如果参数是数字,则自动转为字符

public List<String> summaryRanges(int[] nums)
{
    ArrayList<String> ans = new ArrayList<>();

    int slow = 0;
    int fast = 0;

    for (; fast < nums.length; fast++)
    {
        if(fast == nums.length - 1 || nums[fast]+1 != nums[fast + 1])
        {
            StringBuilder sb = new StringBuilder();
            sb.append(nums[slow]);

            if(nums[slow] != nums[fast])
                sb.append("->").append(nums[fast]);

            ans.add(sb.toString());

            slow = fast + 1;
        }
    }

    return ans;
}

缺失的区间

题目描述

163. 缺失的区间 - 力扣(LeetCode)

给你一个闭区间 [lower, upper] 和一个 按从小到大排序 的整数数组 nums ,其中元素的范围在闭区间 [lower, upper] 当中。

如果一个数字 x[lower, upper] 区间内,并且 x 不在 nums 中,则认为 x 缺失

返回 准确涵盖所有缺失数字最小排序 区间列表。也就是说,nums 的任何元素都不在任何区间内,并且每个缺失的数字都在其中一个区间内。

示例 1:

输入: nums = [0, 1, 3, 50, 75], lower = 0 , upper = 99
输出: [[2,2],[4,49],[51,74],[76,99]]
解释:返回的区间是:
[2,2]
[4,49]
[51,74]
[76,99]

示例 2:

输入: nums = [-1], lower = -1, upper = -1
输出: []
解释: 没有缺失的区间,因为没有缺失的数字。

提示:

  • -109 <= lower <= upper <= 109
  • 0 <= nums.length <= 100
  • lower <= nums[i] <= upper
  • nums 中的所有值 互不相同
public List<List<Integer>> findMissingRanges(int[] nums, int lower, int upper)
{
    List<List<Integer>> missingRanges = new ArrayList<>();

    //[lower, upper]缺失
    if (nums.length == 0)
    {
        missingRanges.add(generateRange(lower, upper));
        return missingRanges;
    }

    //[lower, nums[0] - 1]缺失
    if (nums[0] > lower)
    {
        missingRanges.add(generateRange(lower, nums[0] - 1));
    }

    //i = 0起,[nums[i] + 1, nums[i + 1] - 1]缺失
    for (int i = 0; i < nums.length - 1; i++)
    {
        if(nums[i + 1] - nums[i] > 1)	//非连续递增
        {
            missingRanges.add(generateRange(nums[i] + 1, nums[i + 1] - 1));
        }
    }

    //[nums[length - 1] + 1, upper]缺失
    if (nums[nums.length - 1] < upper)
    {
        missingRanges.add(generateRange(nums[nums.length - 1] + 1, upper));
    }

    return missingRanges;	//返回所有缺失区间
}

//生成区间
public List<Integer> generateRange(int start, int end)
{
    ArrayList<Integer> range = new ArrayList<>();
    range.add(start);
    range.add(end);

    return range;
}

字符串替换空格

题目描述

(剑指offer)请实现一个函数,将一个字符串中的每个空格替换成**“%20”。例如,“We Are Happy.”** 经过替换之后为**“We%20Are%20Happy.”**

public class replaceSpaces
{
    public static void main(String[] args)
    {
        String str1 = replaceSpace_immutable("We Are Happy.");
        System.out.println(str1);    //We%20Are%20Happy.(正确)

        StringBuffer sb = new StringBuffer("We Are Happy.");
        String str2 = replaceSpace_variable(sb);
        System.out.println(str2);   //We%20Are%20Happy.(正确)

    }

    //存储字符串的空间不可变,或者存储空间不算大
    public static String replaceSpace_immutable(String str)
    {
        String res = "";

        for (int i = 0; i < str.length(); i++)
        {
            char c = str.charAt(i);

            if (c == ' ')
                res = res + "%20";
            else
                res = "%s%s".formatted(res, c); //效果相同
        }

        return res;
    }

    //存储字符串的空间可变,或者存储空间很大
    public static String replaceSpace_variable(StringBuffer str)
    {
        int blank = 0;  //str中的空格数
        //计算空格数
        for (int i = 0; i < str.length(); i++)
            if (str.charAt(i) == ' ')
                blank++;

        int fast = str.length() - 1;    //fast指向原长度的末尾

        //设置新的长度(StringBuffer才有的方法,String没有)
        str.setLength(str.length() + 2 * blank);

        int slow = str.length() - 1;    //slow指向新长度的末尾

        while (fast >=0 && fast < slow)
        {
            char c = str.charAt(fast);

            if (c == ' ')
            {
                str.setCharAt(slow,'0');	//复制
                slow--;
                str.setCharAt(slow,'2');
                slow--;
                str.setCharAt(slow,'%');
            }
            else
                str.setCharAt(slow, c);

            fast--;
            slow--;
        }

        return str.toString();
    }
}

replaceSpace_variable图解

在这里插入图片描述

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

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

相关文章

IoT 物联网常用协议

物联网协议是指在物联网环境中用于设备间通信和数据传输的协议。根据不同的作用&#xff0c;物联网协议可分为传输协议、通信协议和行业协议。 传输协议&#xff1a;一般负责子网内设备间的组网及通信。例如 Wi-Fi、Ethernet、NFC、 Zigbee、Bluetooth、GPRS、3G/4G/5G等。这些…

ArkTS - @Prop、@Link

一、作用 Prop 装饰器 和Link装饰器都是父组件向子组件传递参数&#xff0c;子组件接收父组件参数的时候用的&#xff0c;变量前边需要加上Prop或者Link装饰器即可。&#xff08;跟前端vue中父组件向子组件传递参数类似&#xff09; // 子组件 Component struct SonCom {Prop…

python实现Ethernet/IP协议的客户端(二)

Ethernet/IP是一种工业自动化领域中常用的网络通信协议&#xff0c;它是基于标准以太网技术的应用层协议。作为工业领域的通信协议之一&#xff0c;Ethernet/IP 提供了一种在工业自动化设备之间实现通信和数据交换的标准化方法。python要实现Ethernet/IP的客户端&#xff0c;可…

影视后期: PR调色处理,调色工具面板介绍

写在前面 整理一些影视后期的相关笔记博文为 Pr 调色处理&#xff0c;涉及调色工具面板简单认知包括 lumetri 颜色和范围面板理解不足小伙伴帮忙指正 元旦快乐哦 _ 名词解释 饱和度 是指色彩的鲜艳程度&#xff0c;也被称为色彩的纯度。具体来说&#xff0c;它表示色相中灰色…

python实现平滑线性滤波器——数字图像处理

原理&#xff1a; 平滑线性滤波器是一种在图像处理中广泛使用的工具&#xff0c;主要用于降低图像噪声或模糊细节。这些滤波器的核心原理基于对图像中每个像素及其邻域像素的线性组合。 邻域平均&#xff1a; 平滑线性滤波器通过对目标像素及其周围邻域像素的强度值取平均来工…

Linux驱动学习—ioctl接口

1、unlock_ioctl和ioctl有什么区别&#xff1f; kernel 2.6.36 中已经完全删除了struct file_operations 中的ioctl 函数指针&#xff0c;取而代之的是unlocked_ioctl 。ioctl是老的内核版本中的驱动API&#xff0c;unlock_ioctl是当下常用的驱动API。unlocked_ioctl 实际上取…

易舟云财务软件使用教程【文章目录】

易舟云财务软件使用教程【文章目录】 1、财务软件导论2、易舟云财务软件3、财务软件原理4、账套5、会计凭证6、资金日记账7、发票8、员工工资9、固定资产10、期末处理(结转与结账)11、会计账簿12、财务报表13、财务软件设置 1、财务软件导论 财务软件导论 2、易舟云财务软件 …

STM32与TB6612电机驱动器的基础入门教程

TB6612是一款常用的双路直流电机驱动芯片&#xff0c;适用于小型机器人以及其他需要控制电机方向和转速的应用。在STM32微控制器的配合下&#xff0c;可以实现对TB6612电机驱动器的控制&#xff0c;进而实现电机的控制。本文将带领读者一步步了解如何搭建基于STM32与TB6612的电…

Android 13 - Media框架(29)- MediaCodec(四)

上一节我们了解了如何通过 onInputBufferAvailable 和 getInputBuffer 获取到 input buffer index&#xff0c;接下来我们一起学习上层如何拿到buffer并且向下写数据的。 1、获取 input Buffer 获取 MediaCodec 中的 buffer 有两种方式&#xff0c;一种是调用 getInputBuffers…

Linux:/proc/sys/vm/目录各文件详解

目录 前言一、/proc/sys/vm/目录各文件二、相关功能的API函数 前言 /proc/sys/vm/ 目录是 Linux 系统中的一个特殊目录&#xff0c;它包含了与虚拟内存子系统相关的系统内核参数。这些参数可以用来配置系统的虚拟内存管理策略&#xff0c;包括内存分配、页面置换、内存压缩、NU…

【软件工程】航行敏捷之路:深度解析Scrum框架的精髓

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; 软件工程 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 Scrum&#xff08;敏捷开发框架之一&#xff09; 详细介绍和解释&#xff1a; 优缺点&#xff1a; 优点&#xff1a; 缺点&…

2.3 设计FMEA步骤三:功能分析

2.3.1 目的 设计功能分析确保要求/规范中的功能适当分配给系统要素。分析必须使用功能术语进行编写。 功能分析地主要目标是: 产品或过程功能可视化。制作功能树/网或功能分析表格和参数图(P图)。展开与顾客(内部和外部)功能相关的要求。将要求或特性与功能关联。促进工…

cocos creator + vscode debug

安装插件 安装插件&#xff1a;JavaScript Debugger 配置 7456 为本地cocos creator的启动端口 启动debug调试 选择对应的启动方式

计算机网络第一课

先了解层级&#xff1a; 传输的信息称为协议数据单元&#xff08;PDU&#xff09;&#xff0c;PDU在每个层次的称呼都不同&#xff0c;见下图&#xff1a;

关于MySQL Cluster

目录 1.MySQL Cluster2.MySQL Cluster架构3.MySQL Cluster 与 MySQL 主从架构有什么区别4.参考 MySQL Cluster是MySQL的一个高可用性&#xff0c;高性能的分布式数据库解决方案。它结合了内存数据库和共享无状态架构的技术&#xff0c;提供了99.999%的可用性&#xff0c;满足严…

三、C语言中的分支与循环—循环嵌套 (9)

嵌套循环指的是一个循环内部包含另一个循环。外层循环每执行一次&#xff0c;内层循环会执行完其所有的迭代。嵌套循环经常被用来处理多维数据结构&#xff0c;如多维数组&#xff0c;或者在进行复杂的算法操作时&#xff0c;如排序和搜索算法。 嵌套循环可以是任意类型的循环…

excel统计分析——单因素方差分析

参考资料&#xff1a;生物统计学 方差分析是将总变异分解为组间变异的方差和组内变异的方差&#xff0c;并通过F检验推断处理效应是否显著的过程&#xff0c;而方差是通过平方和与自由度计算出来的&#xff0c;所以方差分析首先需要进行平方和与自由度的分解。具体步骤如下&…

RedisTemplate序列化

SpringBoot整合Redis&#xff0c;配置RedisTemplate序列化。如果使用StringRedisTemplate&#xff0c;那么不需要配置序列化&#xff0c;但是StringRedisTemplate只能存储简单的String类型数据&#xff0c;如图&#xff1a; 如果使用StringRedisTemplate存储一个常规对象&#…

【Unity入门】MenuItem 和 ContextMenu 的使用方法

目录 一、ContextMenu描述使用示例ContextMenuItem使用示例 二、MenuItem描述使用示例 三、MenuItem 和 ContextMenu 的区别 一、ContextMenu 描述 ContextMenu 属性用于向上下文菜单添加命令。 在该附加脚本的 Inspector 中&#xff0c;当用户选择该上下文菜单时&#xff0c…

RO-NeRF论文笔记

RO-NeRF论文笔记 文章目录 RO-NeRF论文笔记论文概述Abstract1 Introduction2 Related Work3 Method3.1 RGB and depth inpainting network3.2 Background on NeRFs3.3 Confidence-based view selection3.4 Implementation details 4 Experiments4.1 DatasetsReal ObjectsSynthe…