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则不是快乐数。
结尾
最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!