零的奇幻漂移:解密数组中的神秘消失与重生

零的奇幻漂移:解密数组中的神秘消失与重生_第1张图片

本篇博客会讲解力扣“283. 移动零”的解题思路,这是题目链接。

零的奇幻漂移:解密数组中的神秘消失与重生_第2张图片

思路1

这道题目很有意思。虽然是简单题,其蕴含的玄机还是很多的。正常来讲,这种题目一般都会原地操作(不开辟额外的数组,空间复杂度是O(1)),并且通过一次遍历来完成,这样效率是最高的。我拿到这道题的第一反应是,可以把前面的0都删了,然后后面再补0。具体来说:

  1. 使用2个下标,分别是fast和slow。
  2. fast负责在前面跑,寻找非0数,丢给slow,slow再往后走。

一开始slow是0,显然左边没有0。接着,slow一般不动,除非fast丢了个非0数给slow,slow才会往后走,此时slow左边也没有0。直到fast把数组遍历完,slow的左边都不会有0,且slow的左边就是原来数组的非0数,并且保持原来的顺序。最后从slow的位置开始,到数组末尾的数据全部都改成0即可。

void moveZeroes(int* nums, int numsSize) {
	int fast = 0;
	int slow = 0;
	// 把非0数搬运到左边,删除0
	for (fast = 0; fast < numsSize; ++fast)
	{
		if (nums[fast])
		{
			nums[slow++] = nums[fast];
		}
	}

	// 后面补0
	for (; slow < numsSize; ++slow)
	{
		nums[slow] = 0;
	}
}

零的奇幻漂移:解密数组中的神秘消失与重生_第3张图片

思路2

思路1运用了较为取巧的思路,既然要把0扔到最后面,不如把非0数扔到前面(即删除0),后面再补0,这是一种覆盖的思路。

我们还可以从“交换”的角度来思考这个问题。还是双指针,分别是left和right。我们规定,left的左边没有0,left到right之间都是0。有没有发现,这和快速排序的单趟排序非常的像?在快速排序中,我们会选择一个key,在单趟排序后,保证key左边的值比key小,右边的值比key大,一个经典的思路就是,使用双指针left和right,每次交换后,都保证left左边的值比a[left]小,右边的值比a[left]大。当然,如果你没有联想到快速排序,也是可以解决这道题目的。我们可以这样思考:

  1. 一开始left和right都是0,满足left的左边没有0的条件。
  2. 接着,若right的值非0,则交换left和right,再让left和right向右移动,此时left的左边依然没有0。
  3. 如果right的值一直都非0,就会一直进行第2点的操作,从而left和right不会错开。
  4. 若right的值是0,则left不动,right向右移动,依然满足left的左边没有0的条件。
  5. right会遍历完整个数组,并且只遍历一遍,所以无论如何都要向右移动。
void moveZeroes(int* nums, int numsSize){
	int left = 0;
	int right = 0;
	while (right < numsSize)
	{
		// 保证left左边全是非0数
		if (nums[right])
		{
			// 交换左右下标
			int tmp = nums[left];
			nums[left] = nums[right];
			nums[right] = tmp;

			++left;
		}

		// 不管nums[right]是不是0,right都要向右移动
		++right;
	}
}

零的奇幻漂移:解密数组中的神秘消失与重生_第4张图片

总结

移动0有两种思路:

  1. 把0直接覆盖删除,最后在数组的末尾补0。
  2. 使用单趟快排的思路,不断交换,把非0数换到左边,0换到右边。

感谢大家的阅读!

你可能感兴趣的:(力扣刷题,c语言,算法,开发语言,力扣,leetcode)