Leetcode刷题 由浅入深之哈希法——202. 快乐数
目录
(一)快乐数的C++实现
题意理解
写法一(哈希表)
写法二(栈与列表)
(二)复杂度分析
时间复杂度
空间复杂度
(三)总结
【题目链接】202.快乐数 - 力扣(LeetCode)
编写一个算法来判断一个数
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提示:
1 <= n <= 231 - 1
(一)快乐数的C++实现
题意理解
题目是为了判断一种数叫做快乐数,如果对这个数不停地求每一位平方的和,如果最终结果为1,则是快乐数。
写法一(哈希表)
解题思路:
根据题意,我们可以知道关键有以下几个:
(1)获取数的每一位并求平方的和。我们可以通过数学方式解决,假设有一个数为n,则n%10为最后一位的值,n/10为将最后一位去掉。通过不断进行这两步,我们就可以得到n这个数的每一位上的数字,设置一个变量sum将平方的和叠加起来。
(2)和的循环过程。循环条件则为快乐数的判断标准,是否为1,如果是1就返回true;如果不是1就一直进行循环。最大的问题在于如何判断一个数不可能是快乐数,也就是循环何时停止的问题。
此时,我们就需要将判断过的和存储下来,每计算出一个新的和时,就去找在前面是否出现过,如果出现过,则说明是会无限循环的,那么这个数就不可能是快乐数,此时就返回false。
那么我们需要快速查找之前是否出现过,显然哈希表会比数组的时间效率更高一些。(哈希表的基础知识见博客Leetcode刷题 由浅入深之哈希表——349. 两个数组的交集-CSDN博客)
class Solution {
public:bool isHappy(int n) {set<int> hash_list;while(n != 1){ //控制判断是否为快乐数int sum = 0;while(n){ //遍历每一位数字int b = n % 10;n = n / 10;sum = sum + pow(b, 2); //计算每一位平方的和}n = sum;if(hash_list.find(n) != hash_list.end())return false; //能找到则说明是无限循环,不可能是快乐数hash_list.insert(n);}return true;}
};
写法二(栈与列表)
解题思路:
解题思路与写法一相同,只是使用的数据结构略有不同。
(1)计算完数字的每一位,可以将其存储在栈中,再不断获取栈顶元素进行求平方的和。(此题可以不需要存储,这里只是练习一下栈结构的用法)
(2)在查询新的和在之前是否出现过时,可以采用循环遍历数组的查找方法。(采用写法一的哈希表会查找更快一些,不过使用简单的数组也是可以完成题目的)
class Solution {
public:bool isHappy(int n) {vector<int> list(0);stack<int> nums;while(n != 1){while(n){ //将每一位的数字存储在栈中nums.push(n % 10);n = n / 10;}while(!nums.empty()){n = n + pow(nums.top(), 2);nums.pop();}for(auto i : list){ //迭代器遍历查找元素if(i == n)return false;}list.push_back(n);}return true;}
};
(二)复杂度分析
时间复杂度
(1)写法一:
内层while(n)
循环每次迭代都会将n
除以 10,其时间复杂度为 O(lgn)。
外层while(n != 1)
根据数学证明,不会无限进行下去。在最坏情况下,时间复杂度可以近似看作 O(lgn),因为每次计算新的n
时,其位数大致以对数级别减少。
(位数与数值的对数关系:一个 k 位数 n 满足 ,对不等式两边取以 10 为底的对数可得 k−1≤lgn<k,也就是 k≈lgn。)
set哈希法
的查找和插入操作的平均时间复杂度为O(logk),其中k
是set
中元素的数量。
综上,在最坏情况下,整体时间复杂度为。
(2)写法二:
内层while(n)
循环每次迭代都会将n
除以 10,其时间复杂度为 O(lgn)。
内层while(!nums.empty())
循环用于计算栈中元素的平方和,时间复杂度同样为 O(lgn)。
内层遍历vector数组
看是否已经出现过,时间复杂度为 O(k),k
是vector
中元素的数量。
外层while(n != 1)
循环会持续运行,直到出现重复。时间复杂度可以近似看作 O(logn)。
综上,在最坏情况下,整体时间复杂度为。
空间复杂度
(1)写法一:使用set<int>
存储已经出现过的数字。在最坏情况下,set
中会存储所有在计算过程中出现过的不同数字。因此,空间复杂度为。
(2)写法二:使用vector<int>
和stack<int>
。stack
在每次计算新的n
时会存储n
的每一位,其空间复杂度为 O(lgn)。vector
用于存储已经出现过的数字,在最坏情况下,空间复杂度为 O(lgn)。因此,整体空间复杂度为。
(三)总结
(1)set类型哈希法底层实现为红黑树,查找的时间复杂度会比暴力遍历低。(set类型哈希法知识见Leetcode刷题 由浅入深之哈希表——349. 两个数组的交集-CSDN博客)
(2)注意本题目当中循环求出整数n的每位数字的方法。
学习中,诚挚希望有心者指正和交流,经验或者方法都可。