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

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

  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--;}elseleft++;}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";elseres = "%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,'%');}elsestr.setCharAt(slow, c);fast--;slow--;}return str.toString();}
}

replaceSpace_variable图解

在这里插入图片描述

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

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

相关文章

如何使用批量重命名技巧:将文件名称中文翻译成英文

在日常生活和工作中&#xff0c;经常要处理大量的文件&#xff0c;其中很多文件的名称是中文的。有时&#xff0c;要更好地与国际接轨或是满足特定的需求&#xff0c;要将这些中文文件名称翻译成英文。一个一个手动翻译不仅效率低下&#xff0c;而且容易出错。此时&#xff0c;…

如何防勒索病毒

2018年8月3日 台积电被曝出在台湾地区的三大厂区遭病毒入侵&#xff0c;直到8月6日台积电官方才发表声明称已经恢复生产。 2019年3月份 全球最大铝制品生产商之一的Norsk Hydro遭遇勒索软件攻击&#xff0c;公司被迫关闭多条自动化生产线&#xff0c;震荡全球铝制品交易市场。…

从公司代码仓库更新代码出现错误

错误&#xff1a; 解决&#xff1a;点击idea右上角&#xff0c;将远程代码和本地合并

YOLOv7独家原创改进:提出一种新的Shape IoU,更加关注边界框本身的形状和尺度,对小目标检测也很友好 | 2023.12.29收录

💡💡💡本文改进:一种新的Shape IoU方法,该方法可以通过关注边界框本身的形状和尺度来计算损失,解决边界盒的形状和规模等固有属性对边界盒回归的影响。 💡💡💡对小目标检测涨点明显,在VisDrone2019、PASCAL VOC均有涨点 收录YOLOv7原创自研 https://blog.csdn…

不知道怎么使用IDEA,一篇文章带你快速上手

前言 IDEA 是由 JetBrains 公司开发的软件产品&#xff0c;全称为 IntelliJ IDEA&#xff0c;一个 Java 语言的集成开发环境。它 —— 在业界被公认为是最好的 Java 开发工具之一&#xff0c;尤其在智能代码助手、代码自动提示、重构、J2EE 支持、Ant、JUnit、CVS 整合、代码审…

【ModelScope】从入门到进阶

计算机视觉任务 任务&#xff08;Task&#xff09;中文任务&#xff08;Task&#xff09;英文任务说明单标签图像分类image-classification对图像中的不同特征根据类别进行区分通用图像分割image-segmentation识别图像主体与图像背景进行分离文字检测ocr-detection将图像中的文…

MYSQL 深入探索系列六 SQL执行计划

概述 好久不见了&#xff0c;近期一直在忙项目的事&#xff0c;才有时间写博客&#xff0c;近期频繁出现sql问题&#xff0c;今天正好不忙咱们看看千万级别的表到底该如何优化sql。 案例 近期有个小伙伴生产环境收到了告警&#xff0c;有个6千万的日志表&#xff0c;查询耗时大…

nginx 多端口部署多站点

目录 1.进行nginx.conf 2.复制粘贴 3.修改端口及站点根目录 4. 网站上传 1.进行nginx.conf 在 nginx 主要配置文件 nginx.conf 中&#xff0c;server 是负责一个网站配置的&#xff0c;我们想要多个端口访问的话&#xff0c;可以复制多个 server 先进入到 nginx.conf 中 …

编程语言的未来?直观点5年之际!

编程语言的未来&#xff1f;未来5年应该如何发展&#xff01; 随着科技的飞速发展&#xff0c;编程语言在计算机领域中扮演着至关重要的角色。它们是软件开发的核心&#xff0c;为程序员提供了与机器沟通的桥梁。那么&#xff0c;在技术不断进步的未来&#xff0c;编程语言的走…

这些产品手册制作工具,你都值得收藏

产品手册是企业向消费者传达产品信息的重要媒介&#xff0c;它能够直接影响消费者对产品的了解和购买决策。然而&#xff0c;制作一份专业而吸引人的产品手册并非易事&#xff0c;需要一定的设计和排版能力。为了帮助企业和个人更轻松地制作出优质的产品手册&#xff0c;下面将…

【稳定性】浅谈11.11大促之预案演练 | 京东物流技术团队

一、预案演练 预案演练主要解决的问题是&#xff1a;根据单个系统的应急预案&#xff0c;模拟应用系统的一种或多种故障场景&#xff0c;验证系统的可靠性。 1.1、预案演练形式 预案演练根据应急预案组织相关的应急组织机构和人员&#xff0c;针对事先假设的异常应急场景&am…

二维码智慧门牌管理系统升级解决方案:返工返修作业区域

文章目录 前言一、返工返修作业区域的重要性与内容二、提高作业效率三、确保数据准确性四、可维护性与可扩展性五、未来展望 前言 随着城市的不断发展和居民生活水平的提高&#xff0c;门牌管理系统也得到了持续的升级和改进。其中&#xff0c;二维码智慧门牌管理系统近期也迎…