欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > [Lc优选算法] 双指针 | 移动零 | 复写零 | 快乐数

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

2025/3/1 13:51:13 来源:https://blog.csdn.net/2301_80171004/article/details/145915942  浏览:    关键词:[Lc优选算法] 双指针 | 移动零 | 复写零 | 快乐数

目录

🎢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. 快慢指针方法:一旦确定了循环的存在,就可以 使用快慢指针的方法来检测循环。这种方法通常用于链表中的环检测,但在这里也可以用来识别数字序列何时开始重复。快指针每次走两步,慢指针每次走一步,如果存在循环,两个指针最终会在环内相遇。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词