283. 移动零
给定一个数组
nums,编写一个函数将所有0移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入:
nums = [0,1,0,3,12]输出:[1,3,12,0,0]示例 2:
输入:
nums = [0]输出:[0]提示:
1 <= nums.length <= 10(4)
-2(31) <= nums[i] <= 2(31) - 1**进阶:**你能尽量减少完成的操作次数吗?
cpp
class Solution {
public:
void moveZeroes(vector<int>& nums) {
for (int dest = -1, cur = 0; cur < nums.size(); cur++)
if (nums[cur])
swap(nums[++dest], nums[cur]);
}
};
我们利用dest和cur将数组还分成三个部分,[0,dest][dest+1,cur-1][cur,n-1],赋予这三个部分实际的意义,[0,dest]部分全都是非零元素,[dest+1]全都是0元素,[cur,n-1]是还没有处理的元素。
我们遍历所有元素,一直维护我们赋予的意义。到最后cur=n,数组被dest划分成两个部分,前者全都是非零元素,后者全都是零元素。接着只需要控制维护的代码,使得前者元素相对位置不发生改变即可。
如果nums[cur]==0,我们只需要cur++即可,这样nums[cur]直接进入到[dest+1,cur-1]区间中。
如果nums[cur]!=0,我们需要将这个元素加入到[0,dest]区间中,并且不改变相对位置,也就是需要加入到[0,dest]区间末尾。我们可以先dest++,然后交换nums[dest],nums[cur]元素,cur++,这样[0,dest]区间维护成功,并且[dest+1,cur-1]全都是零元素。全部都维护成功。
1089. 复写零
给你一个长度固定的整数数组
arr,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。注意:请不要在超过该数组长度的位置写入元素。请对输入的数组 就地进行上述修改,不要从函数返回任何东西。
示例 1:
输入: arr = [1,0,2,3,0,4,5,0] 输出: [1,0,0,2,3,0,0,4] **解释:**调用函数后,输入的数组将被修改为:[1,0,0,2,3,0,0,4]
示例 2:
输入: arr = [1,2,3] 输出: [1,2,3] **解释:**调用函数后,输入的数组将被修改为:[1,2,3]
提示:
1 <= arr.length <= 10(4)
0 <= arr[i] <= 9
cpp
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
int cur = 0, dest = -1,
n = arr.size(); // cur dest对应对cur复写操作后 cur对应的位置
while (cur < n) {
if (arr[cur])
dest++;
else
dest += 2;
if (dest >= n - 1)
break;
cur++;
}
if (dest == n) {
arr[n - 1] = 0;
cur--;
dest -= 2;
}
while (cur >= 0) {
if (arr[cur])
arr[dest--] = arr[cur--];
else {
arr[dest--] = 0;
arr[dest--] = 0;
cur--;
}
}
}
};

(图片思路里面的dist写错了,实际上是dest。)
这段代码目的是在给定的整数数组 arr 中,每遇到一个0就在其后面插入一个额外的0,同时保持数组的大小不变,这意味着数组末尾的元素会被推出数组外。
int cur = 0, dest = -1, n = arr.size();这一行代码初始化了三个变量:cur、dest和n。[0,cur]表示待复写的数字,dest对应cur还没有复写的位置,也就是cur对应复写的位置。一直维护这个意义。
while (cur < n) {这个循环用于确定在不实际插入0的情况下,数组中的每个元素在扩展后的数组中的位置。[0,cur]表示已经复写过后的数字,dest对应[0,cur]复写过后的最后一个元素的位置。为了寻找最后一个复写数字的位置。
if (arr[cur]) dest++; else dest += 2;对于当前元素arr[cur],如果它不是0,dest只需增加1(因为当前元素不需要额外空间);如果它是0,则dest需要增加2,因为我们要在数组中为这个0额外插入一个0。
if (dest >= n - 1) break;这个条件判断是为了结束循环,当dest的值表明数组已经填充到最后或者要超出其原始大小时。
cur++;移动到下一个要处理的元素。
if (dest == n) { arr[n - 1] = 0; cur--; dest -= 2; }这一段特殊处理是为了应对dest走出数组界限的情况。
while (cur >= 0) {这个循环从数组的末尾开始,根据之前计算的dest位置,反向复制元素,以便在遇到0时能够正确地插入额外的0。[0,cur]表示待复写的数字,dest对应cur还没有复写的位置,也就是cur对应复写的位置。一直维护这个意义。
if (arr[cur]) arr[dest--] = arr[cur--]; else { arr[dest--] = 0; arr[dest--] = 0; cur--; }这里检查当前元素arr[cur],如果它不是0,就简单地将其复制到dest指示的位置,并同时更新dest和cur。如果当前元素是0,则在dest指示的位置设置两个0,并只更新cur一次。
时间复杂度分析:
第一个循环的时间复杂度为O(n),因为它至多遍历一次数组。
第二个循环也是O(n),因为它最多从数组的末尾遍历到开头。
因此,总的时间复杂度是O(n)。
空间复杂度分析:
这个算法只使用了几个变量(cur, dest, n)来追踪索引和数组的大小,没有使用额外的数据结构,因此空间复杂度为O(1)。
202. 快乐数
编写一个算法来判断一个数
n是不是快乐数。「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果这个过程 结果为 1,那么这个数就是快乐数。
如果
n是 快乐数 就返回true;不是,则返回false。示例 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
示例 2:
输入: n = 2 **输出:**false
提示:
1 <= n <= 2(31)-1
cpp
class Solution {
public:
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);
fast = bitsum(bitsum(fast));
}
return slow == 1;
}
};
先通过题目给的两个实例进行模拟,寻找规律与发现。
n=19,1^2+9^2=82,8^2+2^2=68,6^2+8^2=100,1^2+0+0=1,是快乐数。
n=2,2^2=4,4^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=4。我们发现又出现了4这个数,因此会无限的循环下去。
那我们思考,是否存在一个数,他不是快乐数,但是一直变化的数是不会发生循环?
我们可以计算一下n的范围,2^31-1=2147483647≈2.1x10^9,也就是这个最大数是10位数,如果有一个10位数全都是9的额数,9999999999,我们很容易得到,任意一个n进行一个操作,都会小于9999999999进行一次操作,而10个9操作的结果是9^2=10=810,所以任意一个n进行一次操作的结果一定在[1,810]之间,得到的结果进行一次操作也一定在[1,810]之间。对于一个n对其进行811次操作,那么就一定至少有一次数进行重复出现,所以不可能出现一直变化但是不重复的情况。
鸽巢原理:n个巢,有n+1个鸽,那么就一定有一个巢的鸽子数是2。
所以题目的情况就一定是两种情况,要么是快乐数,要么会重复出现一个数字然后无限循环。

我们可以想象成两种情况,第一种情况会出现环,但是这个环里的数全是1,这是快乐数。第二种情况也会出现环,但是环内的数没有1。因此我们可以使用快慢指针的方法解决这道问题。
快指针走两步,相当于计算两次,慢指针走一步,相当于计算一次,快慢指针一定会相遇,判断相遇时他们的值是多少,如果是1则是快乐数,如果不是1则不是快乐数。
结尾
最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!