最近估计会多写一些算法题目的文章,我也是刚开始学习,写的文章就当记录寒假生活了。如果有初学算法的小伙伴我们可以一起学习,还是新手,有错误的话,还请指正,感谢。
最近就先写双指针类问题了,我使用的编程语言为C++,每个题目我会附上力扣的链接,在文章最后会附上C++代码和Java代码。
移动零
题⽬描述:
给定⼀个数组 nums ,编写⼀个函数将所有 0 移动到数组的末尾,同时保持⾮零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进⾏操作。
示例 1:
输⼊: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
⽰例 2:
输⼊: nums = [0]
输出: [0]
分析可知,该题目就是想要通过对给定的数组进行操作,最终把这个数组分为两部分,前一部分是非0值,后一部分全都是0.
此处我们可以使用一个cur指针来遍历数组(注意双指针的含义不是非得是指针才行,这里我们对数组进行操作,通过cur指针遍历数组,实际上就是通过下标来遍历嘛,所以这里的指针指代的实际上就是下标),再利用一个dest指针来记录非零元素序列的最后⼀个位置。根据 cur 在扫描的过程中,遇到的不同情况,分类处理,实现数组的划分。在 cur 遍历期间,使 [0, dest] 的元素全部都是非零元素, [dest + 1, cur - 1] 的元素全是零。
最终结果如图所示
下标 [0, dest]的元素全为非零元素, [dest + 1, cur - 1] 的元素全是零。
这里我们刚开始将cur指针初始化为0,因为要遍历数组嘛,数组下标就是从0开始的,然后把dest初始化为-1,因为cur还没开始遍历,所以非零元素相当于没有,那就赋值为-1了。之后cur开始一个一个遍历,如果下标为cur的元素为0,那就直接cur++,如果不是0,那就先让dest++,然后再将++过后的dest指向的元素与cur指向的元素互换,达到将非零元素换到前面去的目的,重复上述操作直到cur遍历完整个数组。那么是不是非零元素就全被换到了前面,而零元素就全在后面了呢。
举例
这里以输⼊:nums = [0,1,0,3,12]为例讲解
一开始dest=-1,cur=0,首元素为0,cur++后cur为1,1处元素非0,那就dest++后dest为0,互换后数组变为了 [1,0,0,3,12]。之后cur++后cur为2,2处元素为0,cur直接++变为3,3处元素为非零,那就dest++后dest为1,将下标为1和3的元素互换,此时数组为[1,3,0,0,12]。cur++后cur为4,元素非零,dest先++后dest为2,互换后数组为[1,3,12,0,0]。cur再++后cur为5,已经遍历完数组,结束。
整体过程:
[0,1,0,3,12]
[1,0,0,3,12]
[1,3,0,0,12]
[1,3,12,0,0]
至此我们达到了题目的要求,不知各位是否理解了。
遍历我们用个for循环,很容易解决,元素互换我们可以直接使用C++中自带的swap函数,每次互换前先要dest加1才能互换。所以我们可以用++dest,这就会导致dest会先自增再使用。
以下是C++代码:
class Solution {
public:
void moveZeroes(vector<int>& nums)
{
for(int cur = 0, dest = -1; cur < nums.size(); cur++)
if(nums[cur]) // 处理⾮零元素
swap(nums[++dest], nums[cur]);
}
};
这是运行结果,至于所用时间什么的话,大家不用在意,你多运行几次会发现时间时好时坏。
这里是Java代码:
class Solution
{
public void moveZeroes(int[] nums)
{
for(int cur = 0, dest = -1; cur < nums.length; cur++)
if(nums[cur] != 0) // 仅需处理⾮零元素
{
dest++; // dest 先向后移动⼀位
// 交换
int tmp = nums[cur];
nums[cur] = nums[dest];
nums[dest] = tmp;
}
}
}
时间复杂度:
我们这里就是在不断遍历数组的过程中,以中间的0作为分割,然后左侧是非0元素,右侧是未处理的元素。在处理的过程中我们只是遍历了一次这个数组,所以复杂度为 O ( n )
空间复杂度:
在本题中我们并没有去开出额外的空间,所以复杂度为 O ( 1 )