【算法】必修篇之双指针实战:快乐数 & 盛水最多的容器

【C++】优选算法必修篇之双指针实战:快乐数 & 盛水最多的容器
应用场景
关于双指针的应用场景请看链接直达请点击<---------
目录
1. 快乐数
1.1 题目链接
1.2 题目描述

1.3 题目示例

1.4 算法思路
- 首先在反复计算平方和过程中,数字会变化但是不会一直增大,这是为什么?
当一个数足够大的时候,比如999,它的平方和会小于原数,所以最终会进入一个人循环。比如
4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4这是一个不属于快乐数的循环。
- 所以我们在判断在平方和序列中,判断是否是快乐数的问题需要转变成判断我们最终是到达 1,还是进入某个不含 1 的循环。
- 此时我们就可以运用双指针中的
快慢指针来解决这个问题,因为如果循环他们最终一定会相遇,然后判断他们相遇时候的数字是不是1就可以判断出是不是快乐数了,那么我们怎么实现这个快慢指针呢? - 我们可以让慢指针(初始为这个数) 每次走一步,然后计算一次它的平方和;快指针(初始为这个数的平方和) 一次走两步,计算两次它的平方和;并且每次走完判断他们的值相不相等。
就比如2,最后在42这个数的时候相遇,说明并不是快乐数。

1.5 核心代码实现
//快乐数和盛水最多的容器
#include<iostream>
using namespace std;
class Solution {
public:
int bitSum(int n) //计算给定的数字的平方和用来初始化快指针 和 后续操作的平方和计算
{
int sum = 0;
while (n)//利用循环取出单独每个位置的数字然后求平方进行相加求出平方和
{
int a = n % 10;
sum += a * a;
n /= 10;
}
return sum;
}
bool isHappy(int n) {
int slow = n;//慢指针初始化这个数
int fast = bitSum(n);//快指针初始化为这个数的平方和
while (slow != fast)//循环停止条件:他们相等
{
slow = bitSum(slow);//慢指针走一步
fast = bitSum(bitSum(fast));//快指针走两步
}
return slow == 1;//相等的时候==1就返回正确否则错误
}
};
1.6 示例测试(总代码)
//快乐数和盛水最多的容器
#include<iostream>
using namespace std;
class Solution {
public:
int bitSum(int n) //计算给定的数字的平方和用来初始化快指针 和 后续操作的平方和计算
{
int sum = 0;
while (n)//利用循环取出单独每个位置的数字然后求平方进行相加求出平方和
{
int a = n % 10;
sum += a * a;
n /= 10;
}
return sum;
}
bool isHappy(int n) {
int slow = n;//慢指针初始化这个数
int fast = bitSum(n);//快指针初始化为这个数的平方和
while (slow != fast)//循环停止条件:他们相等
{
slow = bitSum(slow);//慢指针走一步
fast = bitSum(bitSum(fast));//快指针走两步
}
return slow == 1;//相等的时候==1就返回正确否则错误
}
};
int main()
{
int test1 = 19;
int test2 = 2;
int test3 = 1;
cout << test1 << " 是快乐数吗? ";
if (Solution().isHappy(test1)) {
cout << "是" << endl;
}
else {
cout << "否" << endl;
}
cout << test2 << " 是快乐数吗? ";
if (Solution().isHappy(test2)) {
cout << "是" << endl;
}
else {
cout << "否" << endl;
}
cout << test3 << " 是快乐数吗? ";
if (Solution().isHappy(test3)) {
cout << "是" << endl;
}
else {
cout << "否" << endl;
}
return 0;
}

2. 盛水最多的容器
2.1 题目链接
2.2 题目描述

2.3 题目示例

2.4 算法思路
-
首先我们得明白它的容器容积是如何计算的?通过代码示例我们可以发现是用数据中的两个元素中较小的那个乘以他们的下标之差就是他们的容积,所以我们要做的就是通过这个求出其最大值。那么如何计算这个最大值?
-
我们可以设置一个双指针,采用
对撞式指针其中一个指向开头,第二个指向末尾,计算出容积;然后比较两个指针大小并移动较小长度的指针,为什么移动较小的而不移动较长的?
-
通过上图发现,我们的容积是受限于长度小的,移动指针的话他们的下标之差会越来越小,单调递减的;如果我们移动长的其长度最长会保持不变,否则反而可能减小,这样的结果是一直递减的;反之我们移动短的,虽然下标之差在减小,但是我们的长度可能更高,所以选择移动短的。
-
最后在把每次移动后计算的容积进行比较,不断更新那个大的值,最后返回。
2.5 核心代码实现
class Solution {
public:
int maxArea(vector<int>& height) {
int left = 0;
int right = height.size() - 1;
int ret = 0;
while (left < right)
{
int v = min(height[left], height[right]) * (right - left);
ret = max(v, ret);
if (height[left] < height[right]) left++;
else right--;
}
return ret;
}
};
2.6 示例测试(总代码)
#include<iostream>
using namespace std;
#include<vector>
class Solution {
public:
int maxArea(vector<int>& height) {
int left = 0;
int right = height.size() - 1;
int ret = 0;
while (left < right)
{
int v = min(height[left], height[right]) * (right - left);
ret = max(v, ret);
if (height[left] < height[right]) left++;
else right--;
}
return ret;
}
};
int main() {
Solution sol;
vector<int> height1 = { 1,8,6,2,5,4,8,3,7 };
cout << "测试1: " << sol.maxArea(height1) << endl;
vector<int> height2 = { 1,1 };
cout << "测试2: " << sol.maxArea(height2) << endl;
vector<int> height3 = { 4,3,2,1,4 };
cout << "测试3: " << sol.maxArea(height3) << endl;
return 0;
}

总结
在本篇文章中,我们通过「快乐数」与「盛水最多的容器」两个经典题目,深入理解了 双指针(Two Pointers) 的核心思想与进阶应用。
- 快乐数:运用快慢指针检测循环,将数学问题转化为链表环检测问题,巧妙判断数字是否会陷入无限循环
- 盛水最多的容器:采用对撞指针策略,通过移动较短板来寻找最大容量,展现了贪心思想的精妙运用
这两种双指针模式(快慢指针、对撞指针)是算法竞赛和面试中的常客,掌握它们能帮助我们高效解决各类数组和链表问题。
下一篇,我们将继续探索双指针与数学思维的深度结合:
- 📐 有效三角形个数 —— 利用排序+双指针,高效统计满足三角形条件的三元组数量
- 🔢 和为s的两个数字 —— 对撞指针在有序数组中的经典应用,快速寻找目标和的配对元素
敬请期待下一篇:
【C++】优选算法必修篇之双指针实战:有效三角形个数 & 和为s的两个数字
💡 思考题:你能想到还有哪些问题可以用类似的双指针思路解决吗?欢迎在评论区分享你的想法!
更多推荐
所有评论(0)