[Lc优选算法] 双指针 | 移动零 | 复写零 | 快乐数

devtools/2025/2/28 21:34:03/

目录

🎢1.移动零

题解

代码

⭕2.复写零

题解

代码

⭕3.快乐数

题解

代码


🎢1.移动零

题目链接:283. 移动零

题目描述

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

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= nums.length <= 104
  • -231 <= nums[i] <= 231 - 1

进阶:你能尽量减少完成的操作次数吗?


题解

这类题可以归类到一类提里面【数组划分、数组分块】

  • 这类题的特点就是:给一个数组然后制定一个规则,在这个规则下把这个数组划分称若干区间

解决这类题首选:双指针算法 ,数组中用双指针利用数组下标充当指针。

两个指针的作用:

  • cur:从左往右扫描数组,遍历数组
  • dest:已处理区间内,最后一个元素(本题是最后一个非0元素)

两个指针分三个区间:

  • 【0,dest】已处理区间都是非0元素
  • 【dest+1,cur-1】已处理区间都是0元素
  • 【cur,n-1】待处理元素

  • n 个元素 ,下标为 n-1

初始时,cur在下标 0 的位置,dest位-1,可能初始时没有非0元素。

cur 从前往后遍历的过程中:

  1. 遇到 0 元素:cur++
  2. 遇到 非零元素:
    swap(++dest,cur)//将非 0 元素和 ++dest 位置交换,dest 所在区间就为 已处理 非零元素了
    cur++

代码

//如果 cur 不是 0,就和++dest 目标位置 进行交换,实现非 0 元素,都移到前面


⭕2.复写零

题目链接:1089. 复写零

题目描述

给你一个长度固定的整数数组 arr ,请你将该数组中出现的 每个零都复写一遍,并将其余的元素向右平移。

注意:请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改,不要从函数返回任何东西。

示例 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 = [1,2,3]
输出:[1,2,3]
解释:调用函数后,输入的数组将被修改为:[1,2,3]

提示:

  • 1 <= arr.length <= 1040
  • 0 <= arr[i] <= 9

题解

一个数组,不开辟另一个数组,在原数组内解决问题。

  • 解决方法:双指针算法

先根据 “异地” 操作,然后优化成双指针下的 “就地” 操作。

双指针 从左往右,我们发现dest跑到cur前面去了,对于本道题这是不行的。

那试一试从右到左,dest肯定是最后一个数组下标位置,但是cur不好弄,如果初始cur==dest,然后同样会覆盖。

因此我们想办法让cur初始时指向数组最后一个复写的数。如果这样复写

1. 先找到最后一个 “复写” 的数

这里还可以再用一次双指针算法双指针还能套双指针)

查找 就可以 去想 双指针~)

  • 先判断 cur 位置的值
  • 决定 dest 向后走一步或者两步

  • 判断一下 dest 是否已经到结束 为止
  • cur++

但这里有个问题,dest越界的问题,所以要处理一下边界情况。

cur位置的值是0,dest越界,处理越界问题:下标【n-1】复写0,dest-=2,cur-=1;

2.“从后向前” 完成复写操作

代码

//cur 当前未处理 //实现 判断移动
//dest 目标
class Solution {
public://双指针
//先到最后预判
//从右往左void duplicateZeros(vector<int>& arr) {int cur=0,dest=-1;int n=arr.size();//找到 最后一个复写位置while(cur<n){if(arr[cur]){dest++;}else{dest+=2;//为空 就移两步}if(dest>=n-1){break;//到 最后一位 及以后了//就停止 循环}cur++;}//处理 一下边界情况if(dest==n){arr[n-1]=0;cur--;dest-=2;//}//从后往前 填写while(cur>=0){if(arr[cur]){arr[dest--]=arr[cur--];}else{arr[dest--]=0;arr[dest--]=0;cur--;}}}
};

⭕3.快乐数

题目链接:202.快乐数

题目描述

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

示例 1:

输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

示例 2:

输入:n = 2
输出:false

题解

解释:(这里省去计算过程,只列出转换后的数)

2 -> 4 -> 16 -> 37 -> 58 -> 89 -> 145 -> 42 -> 20 -> 4 -> 16

往后就不必再计算了,因为 出现了重复的数字,所以最后结果肯定不会是 1

分析:

为了方便叙述,将【对于一个正整数,每⼀次将该数替换为它每个位置上的数字的平⽅和】这一个操作记为 x 操作;

题目告诉我们,当我们不断重复 x 操作的时候,计算⼀定会【死循环】,死的方式有两种:

  • 情况一:⼀直在 1 中死循环,即 1 -> 1 -> 1 -> 1…

(!重点就是 对于 111 也是一个死循环的理解~)(所以都是循环,只是循环结果不同)

  • 情况二:在历史的数据中死循环,但始终变不到 1

由于上述两种情况只会出现⼀种,因此

只要我们能确定循环是在【情况⼀】中进行,还是在【情况二】中进行,就能得到结果。

简单证明:

  1. 经过一次变化之后的最大值 9^2 * 10 = 810 ( 2^31-1=2147483647 。选一个更大的最大 9999999999 ),也就是变化的区间在 [1, 810] 之间;
  2. 根据【鸽巢原理】,一个数变化 811 次之后,必然会形成一个循环;(*详可见代码后批注)
  3. 因此,变化的过程最终会走到一个圈里,因此可以用【快慢指针】来解决

算法原理:

以前做过一种题,判断链表是否有环,我们用的是快慢双指针,

  1. 定义快慢指针
  2. 慢指针每次向后移动一步,快指针每次向后移动两步
  3. 判断相遇时候的值即可

这里我们不能固定思维,快慢指针是一个思想,可以让不同东西代替指针

这道题我们可以让某个数代替双指针。

代码

class Solution {
public:
// 1.先对 每个位置上的 数字拆分 进行实现
//细分// sum// tmp// nint BitSum(int n) {int sum = 0, a = n;while (a) {int tmp = a % 10;sum += tmp * tmp;a = a / 10;}return sum;}bool isHappy(int n) {// 可以通过bitSum来实现 对每一步的数字和 进行计算了// 2.int slow = n, fast = BitSum(n);while (slow != fast) {slow = BitSum(slow);fast = BitSum(BitSum(fast)); // 移 两步}// 直到 快慢指针 判完环之后return slow == 1;}
};

注:变化的最大值和鸽巢原理

  1. 变化的最大值:考虑到一个数字每一位最大可能是9,因此最大的一位数的平方是81。对于一个十位数而言(尽管实际上由于2^31-1的限制,我们不会遇到这么大的数),其经过一次x操作后的最大可能结果是9^2×10=810。这意味着,无论原始数字多大,经过一次x操作之后,得到的结果都不会超过810。
  2. 鸽巢原理的应用:鸽巢原理(又称抽屉原理)简单地说就是如果有更多的鸽子而鸽巢数量有限,则至少有一个鸽巢里会有多于一只的鸽子。在这个问题中,由于知道经过x操作后数值范围被限制在[1, 810]之间,如果连续进行811次变换,根据鸽巢原理,(只要 变换的次数足够多)至少有一次变换的结果会与之前的某次变换结果相同,从而形成循环
  3. 快慢指针方法:一旦确定了循环的存在,就可以 使用快慢指针的方法来检测循环。这种方法通常用于链表中的环检测,但在这里也可以用来识别数字序列何时开始重复。快指针每次走两步,慢指针每次走一步,如果存在循环,两个指针最终会在环内相遇。

http://www.ppmy.cn/devtools/163448.html

相关文章

Python 爬虫实战案例 - 获取社交平台事件热度并进行影响分析

目录 一、引言 二、数据爬取 三、数据分析 四、可视化展示 五、总结 一、引言 在当今信息爆炸的时代&#xff0c;社交平台成为了各类事件发酵和传播的重要场所。了解社交平台上事件的热度以及其潜在影响&#xff0c;对于舆情监测、市场营销、社会趋势分析等领域具有重要意…

前端性能测试面试题及参考答案

目录 前端性能测试中,首屏时间(FCP)和白屏时间的定义及测量方法是什么? 解释浏览器渲染过程中关键路径(Critical Rendering Path)的组成部分。 如何通过 Navigation Timing API 统计页面加载各阶段耗时? 什么是 LCP(Largest Contentful Paint)?如何优化? 前端性…

C#通过接口 继承接口的类 实现约束 对List内数据类型的值进行排序,可直接复制使用

工具类 通过接口 继承接口的类 实现约束 对List内数据类型的值进行排序,可直接复制使用 //工具类 Tools//说明接口的//1.先有接口 2.继承接口的类 3.实现约束public interface IComParable<T> //接口{int ComPareTo(T other); //在list的数组…

JMeter 的基础知识-安装部分

以下将从环境配置开始,为你详细介绍 JMeter 的基础知识,涵盖环境搭建、界面认知、测试计划创建、常用组件使用等方面内容。 1. 环境配置 1.1 安装 Java JMeter 是基于 Java 开发的,所以需要先安装 Java 开发工具包(JDK)。 下载 JDK:访问 Oracle 官方网站(https://www…

Ubuntu 创建新用户及设置权限

1、新建用户 sudo adduser username 其中username是你要创建的用户的用户名&#xff0c;然后设置密码和相关信息就可以了 2、给新用户sudo权限 新创建的用户没有root权限&#xff0c;我们执行以下命令给用户sudo权限 sudo usermod -a -G adm username sudo usermod -a -G s…

FastExcel 实现数据分批次导入、导出

是基于 FastExcel 实现数据分批次导入和保存的完整解决方案&#xff0c;结合了高性能流式读取与分批处理机制&#xff1a; 一、环境准备 依赖配置 <dependency><groupId>cn.idev.excel</groupId><artifactId>fastexcel</artifactId><version&…

P9420 [蓝桥杯 2023 国 B] 子 2023

P9420 [蓝桥杯 2023 国 B] 子 2023 题目 分析代码 题目 分析 刚拿到这道题&#xff0c;我大脑简单算了一下&#xff0c;这个值太大了&#xff0c;直观感觉就很难&#xff01;&#xff01; 但是&#xff0c;你仔仔细细的一看&#xff0c;先从最简单的第一步入手&#xff0c;再…

JWT+redis实现令牌刷新优化方案

令牌刷新优化方案的详细实现步骤&#xff1a; 1. 令牌服务层改造 1.1 JWT工具类增强 // JwtUtils.java 新增方法 public class JwtUtils {// 生成带动态过期时间的令牌public static String createToken(String subject, String userId, String username, long expirationMi…