题目
题目描述:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
憨憨方法
# 憨到了极限
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
notZero = []
count = 0
while True:
try:
nums.remove(0)
count+=1
except:
break
while True:
if count==0:
break;
else:
nums.append(0)
count-=1
# 优雅了许多,但依然是效率低下的憨憨
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
for i in nums[:]:
if i==0:
nums.append(0)
nums.remove(0)
下面是官方的例子,官方称其为局部空间优化,思路和上面的python代码大同小异,但是效率却差很多。进一步分析代码,python中remove的复杂度为O(n),也就是说上面写的代码的复杂度为O(n^2)
cpp:
void moveZeroes(vector<int>& nums) {
int n = nums.size();
// Count the zeroes
int numZeroes = 0;
for (int i = 0; i < n; i++) {
numZeroes += (nums[i] == 0);
}
// Make all the non-zero elements retain their original order.
vector<int> ans;
for (int i = 0; i < n; i++) {
if (nums[i] != 0) {
ans.push_back(nums[i]);
}
}
// Move all zeroes to the end
while (numZeroes--) {
ans.push_back(0);
}
// Combine the result
for (int i = 0; i < n; i++) {
nums[i] = ans[i];
}
}
根据这个思路,改进了下python的代码,其中遇到了些小插曲qwq,需要再将拷贝这方面好好钻研下了
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
count = 0
notZero = []
for x in nums:
if x == 0:
count+=1
else:
notZero.append(x)
"""注意!直接赋值是引用,copy和deepcopy也会改变地址,
但是和左右两个的地址都不一样?所以输出的既不是notZero里的也不是后来的nums里的,
而是最开始的nums"""
# nums = notZero.copy()
while count:
notZero.append(0)
count-=1
# 采用循环遍历的赋值方法,其他的方法都不行qwq
for i in range(len(nums)):
nums[i] = notZero[i]
空间局部最优,操作局部优化(双指针)
思路:一个记录索引,每次检测到非0元素则+1,将非零元素放到索引处(实际操作时其实是先放,索引再加)。第一次遍历完后记录索引正好到了总长-非零的长度,即索引(包括)之后的所有值都为0。
复杂度=O(n)+O(m)=O(n)
void moveZeroes(vector<int>& nums) {
int lastNonZeroFoundAt = 0;
// If the current element is not 0, then we need to
// append it just in front of last non 0 element we found.
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != 0) {
nums[lastNonZeroFoundAt++] = nums[i];
}
}
// After we have finished processing new elements,
// all the non-zero elements are already at beginning of array.
// We just need to fill remaining array with 0's.
for (int i = lastNonZeroFoundAt; i < nums.size(); i++) {
nums[i] = 0;
}
}
遍历替换 全局最优
空间复杂度: O(1) 吧?只用了几个变量的空间,没有开辟额外的储存空间
时间复杂度: O(n) 从头到尾遍历一次(击败90%+)
思路:两个变量,存索引,然后每次循环交换其值,索引每次循环都会增加,只是其中一个有条件:不为零。所以不难想象,两个索引,分别存的是第一个零的索引,和自然增长的索引,也就是每次循环会将下一个不为零的元素与第一零交换位置,剩下的零自然也就在后面了。实质也只是在局部最优的基础上进行了改进,不要再置零操作。
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
i = j = 0
for i in range(len(nums)):
if nums[i] != 0:
nums[j] , nums[i]= nums[i] , nums[j]
j += 1