提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
3.3、我们来证明一下为什么在闭环中快慢指针的相遇可以解决快乐数的问题?
前言
世上有两种耀眼的光芒,一种是正在升起的太阳,一种是正在努力学习编程的你!一个爱学编程的人。各位看官,我衷心的希望这篇博客能对你们有所帮助,同时也希望各位看官能对我的文章给与点评,希望我们能够携手共同促进进步,在编程的道路上越走越远!
提示:以下是本篇文章正文内容,下面案例可供参考
一、移动零
1.1、题目
1.2、讲解算法原理
这道题目的意思就是要将数组分块,将非零元素移到左边,将零元素移到右边。
我们采用双指针算法,这道题目可以利用数组下标来充当指针,因为数组的下标和指针一样也可以找到所对应的元素。
我们设两个指针:
- cur:从左往右扫描数组,遍历数组;
- dest:已处理的区间内,非零元素的最后一个位置(相当于是非零和零元素之间的一道分界线)
这样我们就可以将数组分为三个区间:
- [0,dest] ---> 非零元素区间;
- [dest+1,cur-1] ---> 零元素区间;
- [cur,n-1] ---> 待处理的区间,我们遍历元素时,要遵循这三个区间的性质。
步骤:
cur从左往右遍历的过程中:
- 遇到0元素:cur++;
- 遇到非零元素:swap(dest + 1,cur);dest++,cur++。
1.3、编写代码
class Solution {
public:
void moveZeroes(vector<int>& nums) {
// 当cur=0时,数组都是未处理区间,此时设dest=-1
for(int cur = 0,dest = -1;cur < nums.size();cur++)
{
if(nums[cur] != 0)
{
swap(nums[++dest],nums[cur]);
}
}
}
};
二、复写零
2.1、题目
2.2、讲解算法原理
双指针算法:
- cur指针指向数组下标为0的位置,用于遍历数组;
- dest指针指向最后一个“复写”的数值的位置,设为-1
步骤:
1、先找到最后一个“复写”的数;
- 先判断cur位置的值;
- 决定dest向后移动一步(cur非零)或者两步(cur零);
- 判断一下dest是否已经到结束位置为止;
- dest还没有结束时,cur++
2、处理一下边界情况(特殊情况,dest加到越界)
3、“从后往前”完成复写操作
2.3、编写代码
class Solution
{
public:
void duplicateZeros(vector<int>& arr)
{
// 1、先找到最后一个“复写”的数
int cur = 0,dest = -1,n = arr.size();
while(cur < n)
{
if(arr[cur] != 0)
dest++;
else
dest+=2;
if(dest >= n-1)// dest有可能加到n,有越界的风险
break;
// dest=n-1,这是最后一个复写数字的位置,cur所处的位置是最后一个复写数字的数值
cur++;
}
// 2、处理一下边界情况(特殊情况,dest加到越界)
if(dest == n)
{
arr[n -1] = 0;// 只需要处理一下n-1的情况,只要对n所在的位置不修改,就不算越界
dest -= 2;
cur--;
}
// 3、从后往前完成复写操作
while(cur >= 0)
{
if(arr[cur] != 0) arr[dest--] = arr[cur--];
else
{
arr[dest--] = 0;
arr[dest--] = 0;
cur--;
}
}
}
};
三、快乐数
3.1、题目
3.2、讲解算法原理
我们可以把这两种情况看作一种情况,即这两种情况都会形成一个闭环,不过第一种情况下的闭环中的每一位都是1;第二种情况下的闭环中的每一位数都不是1。
解题步骤:
- 快慢双指针:并不一定真的有两个指针,这是一种思想。比如:对于数组而言,我们把数组的下标当作指针;这道题,我们将数字当作指针。
- 定义快慢指针;
- 慢指针每次向后移动一步,快指针每次向后移动两步;
- 判断相遇的时候的值即可。
3.3、我们来证明一下为什么在闭环中快慢指针的相遇可以解决快乐数的问题?
鸽巢原理(抽屉原理):n个巢穴,n+1个鸽子,至少有一个巢穴,里面的鸽子数大于1。
将对于⼀个正整数,每一次将该数替换为它每个位置上的数字的平方和视为f操作。
题目中的取值范围是1~2.1*10^9,我们不妨取一个更大的数字,10个9,1~10个9的整数范围已经包括了题目中的取值范围。我们对这个整数范围进行f操作,会得到[1,810]的范围(巢穴);从整数中随机取一个整数x,则对x进行超过810次的f操作,必会有一个[1,810]这个范围内相同的值,那么判断该值是否为1即可。
3.4、编写代码
class Solution
{
public:
// 返回数字n的每一位的平方和
int bitsum(int n)
{
int sum = 0;
while(n)
{
int t = n % 10;
sum += t * t;
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;
}
};
四、盛水最多的容器(medium)
4.1、题目
4.2、讲解算法原理
利用单调性,使用双指针来解决问题。这里指针是数组的下标。
拿区间[6,2,5,4]来说,体积 = 高度h(根据木桶原理) * 宽度w。
第一根线是6,第二根线是4,根据木桶效应,高度为4,宽度为3,V是12,第一根线向右的话,有两种情况:
- 碰到比4还要小的线,高度降低,宽度始终降低,V降低;
- 碰到比4大的线,高度不变,宽度降低,V降低,我们要的是盛水体积最大的容器,因此4可以划掉,让4线向左移动。
拿区间[1,8,6,2,5,4,8,3,7]来说:
- 第一根线是1,第二根线是7,V是7,7线向左移动的话,高度不变,宽度始终降低,V降低,因此1线可以划掉,1线的指针向右移动。
在两指针向内移动的过程中要计算体积,并不断更新体积。
3.3、代码编写
class Solution
{
public:
int maxArea(vector<int>& height)
{
// 定义两个指针,指向数组的头部和尾部
int left = 0,right = height.size() - 1,ret = 0;
while(left < right)
{
int v = min(height[left],height[right]) * (right - left);
ret = max(ret,v);
// 移动指针
if(height[left] < height[right])
left++;
else
right--;
}
return ret;
}
};
总结
好了,本篇博客到这里就结束了,如果有更好的观点,请及时留言,我会认真观看并学习。
不积硅步,无以至千里;不积小流,无以成江海。