移动零
给定一个数组 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
解法(快慢指针法):
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int slow = 0;;
for (int fast = 0; fast < nums.size(); fast++)
{
if (nums[fast] != 0)
{
swap(nums[slow], nums[fast]);
slow++;
}
}
}
};
这个算法的思想就是让慢指针记录数组中0的位置,快指针找到非0数的位置,并和慢指针所指位置进行交换。快指针会不断自增,而慢指针只会在交换后增加。这样等快指针遍历完数组后,所有的非0数都会被挪到0的前面,即将所有0移动到数组的末尾。
这里只需要理解一点,为什么slow不会把非0数交换到数组后面去。首先slow和fast都是从0开始的,假设数组第一个数不是0,那么第一次交换就是数组原地交换,相当于完全没交换。但是这个时候由于实际上发生了交换,所以slow也自增了,那么这时候slow和fast就还是同一个值。那么如果数组第二个数也不是0,同理slow和fast都会递增,且二者值相同。直到fast和slow遇到第一个0。slow停止自增,fast指向下一个值。这时候slow指向0,而fast指向的可能是0也可能不是0。
如果是0,fast继续自增,而slow不变。如果不是,那么两个指针指向的值进行交换,并且两个指针都自增。那么从这里就可以看出,自从slow遇到第一个0开始,它就不可能再追上fast,会永远指向0,而且slow和fast之间,只可能有0值存在,如果有非0值,那么fast经过的时候就会和slow指向的0进行互换。然后slow指向下一个0值。例如:如果slow和fast只差一个位置的话,slow就指向刚才被交换到fast的0。如果不止一个位置的话,由刚才的分析也可以知道,slow和fast之间是不存在非0数的,slow也会指向下一个0。
那么再回到一开始的假设,现在假设数组第一个数就是0。那么其实情况和第一个数不是0时,slow和fast不断递增,直到遇到第一个0时一样。即slow和fast的值不再相等,后面的变化也都一样。