LeetCode算法日记 - Day 2: 快乐数、盛水最多容器
目录
1. 快乐数
1.1 题目分析
1.2 解法
1.3 代码实现
2. 盛水最多的容器
2.1 题目分析
2.2 解法
2.3 代码实现
1. 快乐数
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
1.1 题目分析
- 操作定义:对于一个正整数 n ,x操作计算其各位数字的平方和。例如,n = 19:
- 第一次操作:1^2 + 9^2 = 1 + 81 = 82
- 第二次操作:8^2 + 2^2 = 64 + 4 = 68,依此类推。
- 循环特性:重复 x 操作时,数据必然进入循环(死循环)。原因如下:
- 设 x 操作后最大可能值为 9^2 \times 10 = 810 (因为10位数的最大值 9999999999 的平方和不超过810)。
- 变化范围被限制在 [1, 810] 区间内。
- 根据鸽巢原理(元素数量超过容器大小时必有重复),经过大于811次操作后,必然出现重复值,形成循环。
- 循环类型:
- 情况一:循环固定于 1 (即 1 \to 1 \to 1 ),此时该数为快乐数。
- 情况二:循环在其他值间发生(如 4 \to 16 \to 37 \to 58 \to 89 \to 145 \to 42 \to 20 \to 4 \cdots),但不涉及 1。
- 两种情况互斥,因此只需检测循环是否在 1 处发生。
1.2 解法
我们可以使用快慢指针来解决,快慢指针是一种高效检测循环的方法。其原理在于:在循环序列中,快指针总会追上慢指针。
算法步骤:
a)初始化指针
- 慢指针 slow :起始于输入数 n 。
- 快指针 fast :起始于 x 操作一次后的值(即 bitSum(int n))
b)移动指针
- 慢指针每次执行一次x操作:slow = bitSum(slow)
- 快指针每次执行两次x操作:fast = bitSum(bitSum(fast))
- 重复此过程,直到 slow 与 fast 相遇。
c)判断结果
- 如果相遇时 slow = 1(或 fast = 1),则是快乐数。
- 否则,不是快乐数。
d)关键函数
x操作的核心是提取一个数$n$的各位数字并计算平方和。方法如下:
- 初始化临时变量 int sum = 0,int t = 0;
- 循环操作:
- 提取个位数字:t = n %10
- 累加平方:sum += t*t
- 更新 n:n = n / 10(整数除法)
- 当 n = 0 时停止,返回 sum。
1.3 代码实现
以下是 Java 的代码实现
class Solution {public int bitSum(int n){int sum = 0;while(n>0){int t = n%10;sum += t*t;n = n/10;}return sum;}public boolean isHappy(int n) {int slow = n;int fast = n;while(true){slow = bitSum(slow);fast = bitSum(bitSum(fast));if(slow == fast){break;}}if(slow == 1){return true;}else{return false;}}
}
2. 盛水最多的容器
11. 盛最多水的容器 - 力扣(LeetCode)
给定一个长度为 n 的整数数组 height。有 n 条垂线,第 i 条线的两个端点是 ( i , 0 ) 和 ( i ,height[
i]
) 。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。i n
说明:你不能倾斜容器。
2.1 题目分析
在解决容器最大容积问题时,给定一个数组 height ,其中 height[i] 表示位置$i$处柱子的高度,目标是找到两个柱子,使它们与 x 轴形成的容器能容纳最多的水。容器的容积由宽度(索引差)和高度(较小柱子高度)决定。对撞指针算法通过双指针高效枚举可能解,避免不必要的计算。
2.2 解法
-
初始化指针:
- 设
left
指针指向数组起始位置(索引0),right
指针指向数组末尾位置(索引len(height)-1
)。 - 初始化最大容积
max_area = 0
。
- 设
-
指针移动逻辑:
- 计算当前容积:int v = Math.min(height[left],height[right])*(right-left);
- 更新体积为当前最大值。设体积 ret = Math.max(ret,v);
- 关键决策:比较 height[left] 和 height[right] :
- 若 height[left]<height[right] ,则移动左指针 left++。
- 原因:较小高度限制了容积。移动较小边界(左边界)可能遇到更高柱子,从而增大容积;而移动右边界只会减小宽度,受限于当前最矮的柱子,只能体积减小。
- 否则 height[left]>height[right] ,移动右指针 right-- 。
- 原因:类似逻辑,移动较小边界(右边界)可能增大容积。
- 若 height[left]<height[right] ,则移动左指针 left++。
- 重复以上过程,直到 left >= right。
-
终止条件:
- 当 left 和 right 相遇时,所有有效组合已枚举完毕,返回 ret。
2.3 代码实现
class Solution {public int maxArea(int[] height) {int left = 0, right = height.length-1,ret = 0;while(left<right){int v = Math.min(height[left],height[right])*(right-left);ret = Math.max(ret,v);if(height[left]<height[right]) left++;else right--;}return ret;}
}