一、快乐数
1. 题目解析
这个题目给了我们一个 “快乐数” 的定义,对于一个整数,每一次将这个数替换为每个位置上(该数的每一位)数字的平方和,然后一直重复这个过程,直到这个数变为1,如果最后的结果为1,那么这个数就是快乐数,如果这个数最后无限循环,或者无法得到1,即这个数不是快乐数。
示例1:
输入: n = 19
输出: true
解释:
1 2 + 9 2 = 82 8 2 + 2 2 = 68 6 2 + 8 2 = 100 1 2 + 0 2 + 0 2 = 1 1^2 + 9^2 = 82\\ 8^2 + 2^2 = 68\\ 6^2 + 8^2 = 100\\ 1^2 + 0^2 + 0^2 = 1 12+92=8282+22=6862+82=10012+02+02=1
这里经过四次变换后,最后的结果变成了1,符合题意,所以19是一个快乐数。
示例二:
输入: n = 2
输出: false
解释:
2 2 = 4 4 2 = 8 8 2 = 16 1 2 + 6 2 = 37 3 2 + 7 2 = 58 5 2 + 8 2 = 89 8 2 + 9 2 = 145 1 2 + 4 2 + 5 2 = 42 4 2 + 2 2 = 20 2 2 + 0 2 = 4 . . . . . . 2^2 = 4\\ 4^2 = 8\\ 8^2 = 16\\ 1^2 + 6^2 = 37\\ 3^2 + 7^2 = 58\\ 5^2 + 8^2 = 89\\ 8^2 + 9^2 = 145\\ 1^2 + 4^2 + 5^2 = 42\\ 4^2 + 2^2 = 20\\ 2^2 + 0^2 = 4\\ ...... 22=442=882=1612+62=3732+72=5852+82=8982+92=14512+42+52=4242+22=2022+02=4......
一直这样运算下去会发现,这个数无限循环下去结果也永远不会等于1,所以这个数不是快乐数
2. 算法原理
其实根据上面题目解析画的图加上如果我们之前做过一道链表是否带环的问题的话,这道题的解法也就出来了,就是快慢双指针。
根据题目的意思可以分为两种情况(一种是变为1,另一种是无限循环始终变不到1):
-
输入一个数,然后按要求发生变换,一直重复变换过程,直到这个数变为1为止。
-
输入一个数,然后按要求发生变换,一直重复变换过程,但这个数永远无法变成1。
注意: 这两种情况可以抽象成一种情况,即一直重复变换过程,如果是第一种情况,那么最后循环成一个环,环理的每个值都是1,而第二种情况也是会一直循环直到形成一个环,只不过环里的每一个数都不是1。
证明:为什么这些数字经过变换后一定会形成环?
首先我们可以看一下这道题的数据范围,数据范围就是整形的最大值。
要证明为什么一定会形成环,需要用到鸽巢原理
鸽巢原理,也被称为抽屉原理,它的基本思想是:如果有n+1个或更多的物体要放入n个容器中,那么至少有一个容器里面包含两个或更多的物体。
这道题的数据范围是2的31次方,也就是2,147,483,648,那我们现在假设将数据定位9,999,999,999,然后让这个数去进行题目所述的变换,也就是10个9的平方相加,最后的结果也就是810,那么根据鸽巢原理,让这个数经过810次变换后,在发生变换,变换后的值的范围一定是 [1,810] 之间的,所以一定会形成一个环
解法:快慢双指针
既然这个数经过变换后一定会形成一个环,所以我们只要利用快慢双指针进入环,直到快慢指针相遇,然后判断相遇的值是不是1就行了
- 1.定义快慢指针:slow,fast
- 2.慢指针每次向后移动一步,快指针每次向后移动两步
- 3.快慢指针相遇后,判断它们的值是否为1即可
示例一情况:
示例二情况:
细节问题:
由于这个操作是循环进行的,循环条件是slow != fast,当由于第一次slow和fast都指向第一个数,循环根本就进不去,所以我们定义fast是可以是第一个数变换后的数,也就是指向slow的后一位,然后继续循环即可
3. 代码编写
C语言代码:
// 返回数字 n 的每一位的平方和
int bitsum(int n)
{
int sum = 0;
while(n)
{
int t = n % 10; // 拿到最后一位
sum += t * t; // 平方后相加
n = n / 10; // 去掉最后一位
}
return sum;
}
bool isHappy(int n)
{
int slow = n, fast = bitsum(n);
// fast要定义成 n 变换后的,否则和 slow 相等无法进入循环
while(slow != fast)
{
slow = bitsum(slow); // slow 往后移动一步
fast = bitsum(bitsum(fast)); // fast 往后移动两步
}
return slow == 1; // slow 和 fast 相遇后判断值是否为一即可
}
C++代码:
class Solution {
public:
// 返回数字 n 的每一位的平方和
int bitsum(int n)
{
int sum = 0;
while(n)
{
int t = n % 10; // 拿到最后一位
sum += t * t; // 平方后相加
n = n / 10; // 移除最后一位
}
return sum;
}
bool isHappy(int n) {
int slow = n, fast = bitsum(n);
while(slow != fast)
{
slow = bitsum(slow); // slow 往后移动一步
fast = bitsum(bitsum(fast)); // fast 往后移动两步
}
return slow == 1; // 判断相遇后的值是否为1
}
};
Python代码:
class Solution:
# 返回数字 n 的每一位的平方和
def bitsum(self, n):
sum = 0
while n != 0:
t = n % 10 # 拿到最后一位
sum += t * t # 平方后相加
n = n // 10 # 移除最后一位
return sum
def isHappy(self, n: int) -> bool:
slow = n
# fast要定义成 n 变换后的,否则和 slow 相等无法进入循环
fast = self.bitsum(n)
while slow != fast:
slow = self.bitsum(slow) # slow 往后移动一步
fast = self.bitsum(self.bitsum(fast)) # fast 往后移动两步
return slow == 1 # slow 和 fast 相遇后判断值是否为一即可
二、盛最多水的容器
1. 题目解析
二、盛水最多的容器【点击跳转】
简单来说就是找出两条线,让他们与X轴共同构成的容器可以容纳最多的水,然后返回容器可以存储的最大水量。
注意: 这里需要注意一点,那就是在计算容器体积时,容器的高度应该选短的那一根线,也就是所谓的 “木桶效应”,另外,不能将容器倾斜。
2. 算法原理
首先可以根据木桶效应找到一个规律:首先是宽度变化,因为我们是不断向内枚举(不断枚举剩余的高度),所以宽度是不断变小的。高度变化,如果向内枚举时,遇到一个比现在高的数,当由于木桶效应,高度只能有较小的数决定,多以高度要么不变要么变小,所以可以直接将较小的数 “干掉”,不需要让他枚举其他数。
根据这个规律,我们的解法也就出来了
解法:左右双指针
解法:左右双指针
- 定义左右双指针 left 和 right,left 和 right 初始化为数组的最左边和最右边
- 计算容器的体积,然后判断height[left] 和 height[right] 值的大小
如果 left 小于 right left 向右移动,反之,right 向左移动,直到两个指针相遇- 将所有的体积都计算出来后取最大值即可
3. 代码编写
C语言代码:
int maxArea(int* height, int heightSize) {
// 1. 定义左右双指针
int left = 0, right = heightSize - 1, ret = 0;
while(left < right)
{
// 2. 求体积, 注意木桶效应, height[left], height[right]取小的进行计算
int v = 0;
if(height[left] < height[right])
{
v = height[left] * (right - left);
left++;
}
else
{
v = height[right] * (right - left);
right--;
}
// 3. 比较容器体积大小
if(v > ret)
ret = v;
}
return ret;
}
C++代码:
class Solution {
public:
int maxArea(vector<int>& height) {
// 1. 定义左右双指针
int left = 0, right = height.size() - 1, ret = 0;
while(left < right)
{
// 2. 求体积, 注意木桶效应, height[left], height[right]取小的进行计算
int v = min(height[left], height[right]) * (right - left);
// 3. 比较容器体积大小
ret = max(ret, v);
// 4. 比较height[left], height[right]大小,移动指针
if(height[left] < height[right]) left++;
else right--;
}
return ret;
}
};
Python代码:
class Solution:
def maxArea(self, height: List[int]) -> int:
# 1. 定义左右双指针
left = 0
right = len(height) - 1
ret = 0
while left < right:
# 2. 求体积, 注意木桶效应, height[left], height[right]取小的进行计算
v = min(height[left], height[right]) * (right - left)
# 3. 比较容器体积大小
ret = max(ret, v)
# 4. 比较height[left], height[right]大小,移动指针
if height[left] < height[right]:
left += 1
else:
right -= 1
return ret