一、题目介绍
这是移动零的网址,大家可以看完博客的思路去练习一下。
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums =[0,1,0,3,12]
输出:[1,3,12,0,0]
示例 2:
输入: nums =[0]
输出:[0]
提示:
1 <= nums.length <= 104
-231 <= nums[i] <= 231 - 1
普通方法:
其实这个很简单,把非零都移到前面去之后,后面剩下的直接补0就好。
void moveZeroes(int* nums, int numsSize)
{
int count = 0;
for (int i = 0; i < numsSize; i++)
{
if (nums[i] == 0)
{
count++;
continue;
}
nums[i - count] = nums[i];
}
while (count-- > 0) {
nums[numsSize - count - 1] = 0;
}
}
实现思路:
1. 初始化计数器:定义一个变量 `count` 来记录数组中零的个数。
2. 遍历数组:使用一个 `for` 循环遍历数组 `nums`。
3. 处理非零元素:在循环中,如果当前元素 `nums[i]` 不是零,就将其复制到索引 `i - count` 的位置。由于 `count` 记录了前面有多少个零,所以 `i - count` 就是当前非零元素应该在的位置。
4. 更新计数器:如果当前元素是零,`count` 就递增,表示遇到了一个零值,但是不进行元素交换,因为零值将在后面统一处理。
5. 填充零值:在 `for` 循环结束后,使用一个 `while` 循环从数组的末尾向前填充零。由于前面已经将所有非零元素移动到了数组的前部,所以数组的末尾 `count` 个位置需要填充零。
6. 更新数组:在 `while` 循环中,每次循环将数组的倒数第 `count` 个位置设置为零,并且 `count` 递减。这样,每次循环都将零放在正确的位置,直到 `count` 变为零。
7. 循环结束:当 `count` 变为零时,所有的零都被移动到了数组的末尾,且非零元素的相对顺序没有改变。
这段代码的关键在于使用一个计数器 `count` 来跟踪零的数量,并在遍历过程中将非零元素移动到数组的前部。然后,使用另一个循环将剩余的位置填充为零。这种方法的时间复杂度是 O(n),其中 n 是数组 `nums` 的长度,因为我们只遍历了数组两次(一次移动非零元素,一次填充零)。空间复杂度是 O(1),因为我们只使用了有限的额外空间(即 `count` 变量)。
双指针方法:
void swap(int *a, int *b) {
int t = *a;
*a = *b, *b = t;
}
void moveZeroes(int *nums, int numsSize) {
int left = 0, right = 0;
while (right < numsSize) {
if (nums[right]) {
swap(nums + left, nums + right);
left++;
}
right++;
}
}
思路及解法
使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。
右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。
注意到以下性质:
左指针左边均为非零数;
右指针左边直到左指针处均为零。
因此每次交换,都是将左指针的零与右指针的非零数交换,且非零数的相对顺序并未改变。
交换法:
void moveZeroes(int* nums, int numsSize) {
// `j` 指向数组中下一个非零元素应该放置的位置
int j = 0;
// 遍历数组
for (int i = 0; i < numsSize; i++) {
// 如果当前元素不是零,则将其移动到 `j` 的位置
if (nums[i] != 0) {
nums[j++] = nums[i]; // 将非零元素复制到 `j` 的位置,并递增 `j`
}
}
// 此时 `j` 指向数组中第一个零的位置
// 从 `j` 开始填充零,直到数组末尾
for (; j < numsSize; j++) {
nums[j] = 0;
}
}
实现思路:
-
初始化指针:定义一个指针
j
,它用来指向数组中下一个非零元素应该放置的位置。 -
遍历数组:使用一个
for
循环遍历数组nums
。 -
处理非零元素:在循环中,如果当前元素
nums[i]
不是零,就将其复制到索引j
的位置。然后递增j
,准备放置下一个非零元素。 -
省略条件判断:由于
j
总是指向下一个非零元素应该放置的位置,所以不需要检查i
和j
是否相等,直接进行赋值和j
的递增。 -
填充零值:在
for
循环结束后,j
将指向数组中第一个零应该放置的位置。此时,使用另一个for
循环从j
开始,将数组的剩余位置填充为零。 -
循环结束:第二个
for
循环继续执行,直到j
达到数组末尾numsSize
,确保所有剩余的位置都被设置为零。 -
原地操作:整个操作都在原数组
nums
上进行,没有使用额外的数组空间,因此是原地(in-place)操作。 -
时间复杂度:这个算法的时间复杂度是 O(n),其中 n 是数组
nums
的长度,因为我们只遍历了数组两次(一次移动非零元素,一次填充零)。 -
空间复杂度:空间复杂度是 O(1),因为我们只使用了有限的额外空间(即
j
变量)。
本期内容向大家介绍了这道OJ题的三种做法,其实这些思想可以贯穿到很多题中,大家平时要多加练习,才能写出更好更妙的代码。加油各位!
标签:count,nums,int,练习,非零,数组,移动,leetcode,指针 From: https://blog.csdn.net/Yyyyyyyyyyyyds/article/details/141176599