当前位置: 首页 > news >正文

优选算法 力扣 202.快乐数 快慢双指针 解决带环问题 C++解题思路 每日一题

目录

  • 零、题目描述
  • 一、为什么这道题值得你花几分钟看懂?
  • 二、从“序列特性”到“环模型”:思路的转化
  • 三、快慢指针实现:判断循环与结果
  • 四、完整代码与解析
    • 完整代码(附详细注释)
    • 代码走读(示例1:`n = 19`)
    • 复杂度分析
  • 五、常见问题与解决方案
  • 六、举一反三

零、题目描述

题目链接:快乐数

题目描述:
编写一个算法来判断一个数  是不是快乐数。
「快乐数」定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 true ;不是,则返回 false 。

示例 1:
输入:n = 19
输出:true
解释:
1² + 9² = 82
8² + 2² = 68
6² + 8² = 100
1² + 0² + 0² = 1

示例 2:
输入:n = 2
输出:false

提示:
1 <= n <= 2^31 - 1

一、为什么这道题值得你花几分钟看懂?

快乐数的判定问题,看似是数学序列的游戏,实则藏着算法中“模型转化”的精髓。它的价值在于:让你学会如何把一个看似陌生的问题,套进熟悉的解题框架里

当你计算数字的平方和时,会发现序列的演变路径和链表惊人地相似——每个数字都像一个节点,平方和的计算过程就是节点指向的“指针”。而题目中“无限循环”的特性,本质上就是链表中“环”的具象化。这种转化能力,能帮你打通 快乐数 与 环形链表(LeetCode 141)的思维壁垒:

  • 就像环形链表中用快慢指针判断是否有环一样,快乐数中同样可以用快慢指针捕捉循环
  • 环形链表找环的入口对应快乐数中循环的起点,而1这个特殊值则对应“无环且终点为1”的理想情况

搞定这道题,你不仅能掌握快慢指针的另一种妙用,更能学会“透过现象看本质”——当未来遇到新问题时,会下意识地思考:“它和我学过的哪个模型相似?” 这种联想能力,才是算法进阶的关键。

如果对环形链表的快慢指针玩法还不熟悉,建议先攻克LeetCode 141,再回头看这道题,会有“原来如此”的顿悟感~

二、从“序列特性”到“环模型”:思路的转化

1. 分析快乐数的判定过程

对于一个数n,按照规则计算每个位置上数字的平方和,会得到一个新的数,不断重复这个过程,会形成一个序列。例如对于19:
19 → 82 → 68 → 100 → 1 → 1 → …

这个序列有两种可能的结局:

  • 最终会得到1,之后一直保持1,这样的数就是快乐数。
  • 进入一个循环,永远不会得到1,这样的数就不是快乐数。

2. 发现与链表环的相似性

观察这个序列,每个数都可以看作是一个节点,从一个数到它的平方和的过程就如同链表中的指针指向。如果序列进入循环,就相当于链表中出现了环。

例如对于不快乐的数2:
2 → 4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4 → …
从4开始进入了循环,就像链表中4这个节点之后形成了环。

3. 突破口:用判断链表环的方法解决问题

既然快乐数的判定过程可以转化为判断序列是否有环,以及环的入口是否为1,那么判断链表是否带环的快慢指针法就可以派上用场了:

  • 慢指针每次计算一次平方和。
  • 快指针每次计算两次平方和。
  • 如果最终快慢指针相遇时的值为1,就是快乐数;否则,就不是。

三、快慢指针实现:判断循环与结果

1. 辅助函数:计算数字的平方和

首先需要一个函数来计算一个数每个位置上数字的平方和:

int bitsum(int n)
{int sum = 0;while (n){int t = n % 10; // 取出最后一位数字sum += t * t;   // 累加该数字的平方n /= 10;        // 去掉最后一位数字}return sum;
}

2. 快慢指针的应用

  • 初始化慢指针slow为n,快指针fast为n的平方和(即bitsum(n))。
  • 循环执行:慢指针每次计算一次平方和,快指针每次计算两次平方和,直到快慢指针相等。
  • 最后判断快慢指针相遇时的值是否为1,若是则返回true,否则返回false。
bool isHappy(int n) 
{int slow = n, fast = bitsum(n);while (slow != fast){slow = bitsum(slow);          // 慢指针走一步fast = bitsum(bitsum(fast));  // 快指针走两步}  return slow == 1; // 相遇时为1则是快乐数
}

四、完整代码与解析

完整代码(附详细注释)

class Solution {
public:// 计算一个数每个位置上数字的平方和int bitsum(int n){int sum = 0;while (n){int t = n % 10; // 取出最后一位数字sum += t * t;   // 累加该数字的平方sum += t * t;n /= 10;        // 去掉最后一位数字}return sum;}bool isHappy(int n) {// 初始化慢指针为n,快指针为n的平方和int slow = n, fast = bitsum(n);// 当快慢指针不相等时,继续循环while (slow != fast){slow = bitsum(slow);          // 慢指针每次计算一次平方和fast = bitsum(bitsum(fast));  // 快指针每次计算两次平方和}  // 当快慢指针相等时,如果值为1则是快乐数,否则不是return slow == 1;}
};

代码走读(示例1:n = 19

  1. 初始化:slow = 19fast = bitsum(19) = 1² + 9² = 82
  2. 第一次循环:slow != fast
    • slow = bitsum(19) = 82
    • fast = bitsum(bitsum(82)) = bitsum(8² + 2²) = bitsum(68) = 6² + 8² = 100
  3. 第二次循环:slow != fast
    • slow = bitsum(82) = 68
    • fast = bitsum(bitsum(100)) = bitsum(1² + 0² + 0²) = bitsum(1) = 1
  4. 第三次循环:slow != fast
    • slow = bitsum(68) = 100
    • fast = bitsum(bitsum(1)) = bitsum(1) = 1
  5. 第四次循环:slow != fast
    • slow = bitsum(100) = 1
    • fast = bitsum(bitsum(1)) = bitsum(1) = 1
  6. 此时slow == fast,退出循环,返回slow == 1,即true

复杂度分析

复杂度类型具体值说明
时间复杂度O(log n)对于每次计算平方和,数字的位数大约是log n(以10为底),而快慢指针相遇的次数是有限的,整体可看作O(log n)
空间复杂度O(1)只使用了常数个额外变量

五、常见问题与解决方案

  1. 为什么快慢指针一定会相遇?

    • 因为数字的平方和序列要么最终得到1,要么进入循环。当进入循环时,快指针速度比慢指针快,最终一定会追上慢指针,即相遇。
  2. 为什么相遇时如果是1就是快乐数?

    • 因为如果是快乐数,序列最终会稳定在1,此时快慢指针都会指向1,所以相遇时为1;如果不是快乐数,序列会进入一个不含1的循环,相遇时就不是1。
  3. 计算平方和时需要考虑负数吗?

    • 不需要,因为题目中n是正整数,且平方和的计算结果也一定是非负的,而正整数的平方和计算过程中不会出现负数。

六、举一反三

这道题的快慢指针思路可推广到:

  • 检测循环:任何可能出现循环的序列问题,都可以用快慢指针来判断是否有循环以及循环的位置。
  • 优化查找:在一些需要重复计算的问题中,可通过快慢指针减少计算次数,提高效率。

下一题预告:LeetCode 11. 盛最多水的容器!这道题会让你亲眼见证双指针的 “收缩魔法”—— 明明是看似需要枚举所有可能的问题,却能通过巧妙移动左右边界,用 O (n) 时间找到最优解。想象一下:两根柱子立在坐标系里,如何快速找到间距与高度的完美平衡?明天的解析会带你拆解其中的贪心逻辑,看完你一定会惊叹:“原来还能这么算!”

最后欢迎大家在评论区分享你的代码或思路,咱们一起交流探讨~ 🌟 要是有大佬有更精妙的思路或想法,恳请在评论区多多指点批评,我一定会虚心学习,并且第一时间回复交流哒!

在这里插入图片描述

这是封面原图~ 喜欢的话先点个赞鼓励一下呗~ 再顺手关注一波,后续更新不迷路!
在这里插入图片描述

http://www.dtcms.com/a/313120.html

相关文章:

  • ThinkPHP5x,struts2等框架靶场复现
  • Coin Combinations II(Dynamic Programming)
  • LLM - AI大模型应用集成协议三件套 MCP、A2A与AG-UI
  • 用 Eland 在 Elasticsearch Serverless 部署 Learning-to-Rank 排序模型
  • 数据,正在成为AI大模型最后的护城河
  • leetcode 2106. 摘水果 困难
  • Rust 同步方式访问 REST API 的完整指南
  • 道格拉斯-普克算法 - 把一堆复杂的线条变得简单,同时尽量保持原来的样子
  • python---赋值、浅拷贝、深拷贝
  • 【C 学习】03-你的第一个C程序
  • 上位机知识篇---脚本文件
  • Linux环境下使用Docker搭建多服务环境
  • Corrosion2靶场
  • xxljob总结
  • Obsidian结合CI/CD实现自动发布
  • 1、docker容器命令 | 生命周期管理
  • NX969NX972美光固态闪存NX975NX977
  • python 12 install jupyter时zmq.h或libzmq报错处理
  • MVCC:数据库事务隔离的 “时空魔法”
  • nvm切换本地nodejs环境
  • node中shapefile字符集判断
  • Sklearn 机器学习 数据聚类 KMeans实现聚类
  • wav音频格式中,ACM波形、A/mu-Law Wave、Windows PCM、Microsoft ADPCM的区别
  • 《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——9. 接入真实硬件:驱动USB摄像头
  • LeetCode 分类刷题:2824. 统计和小于目标的下标对数目
  • Go语言--语法基础7--函数定义与调用--自定义函数
  • Go语言实战案例:TCP服务器与客户端通信
  • HoloLens+vuforia打包后遇到的问题
  • 图像、视频、音频多模态大模型中长上下文token压缩方法综述
  • Connection refused: no further information: localhost/127.0.0.1:2375