在python中将所有零移动到数组的末尾

Posted

技术标签:

【中文标题】在python中将所有零移动到数组的末尾【英文标题】:Move all zeros to the end of the array in python 【发布时间】:2021-03-22 03:37:36 【问题描述】:

给定一个数组 nums,我试图将所有 0 移到它的末尾,同时保持非零元素的相对顺序。我正在尝试在不复制数组的情况下就地执行此操作。

所以对于输入 [0,1,0,3,12],输出应该是 [1,3,12,0,0]。但是我下面的代码只能将数组中的第一个零移动到数组的末尾,并给出错误的输出 [1,0,3,12,0]。如何修改它以便所有零有效地移动到数组的末尾?

class Solution:
def moveZeroes(self, nums: List[int]) -> None:
    """
    Do not return anything, modify nums in-place instead.
    """
    n=len(nums)
    for i in range(0,n):
        if (nums[i]==0) and ((i+1)<n): 
            nums[i]=nums[i+1]
            nums[i+1]=0
            print(nums) 

【问题讨论】:

【参考方案1】:

让我做你的功课:-)

n = len(nums)
j = 0
for i in range(n):
    nums[j] = nums[i]
    j += 1 if nums[i] else 0
nums[j:] = [0] * (n-j)

如果你的头脑足够理解它,你至少会学到一些东西......

【讨论】:

【参考方案2】:

Python 的内置排序是稳定排序,这意味着元素只有在根据您用于排序的标准不同时才会被交换。

要使用排序将所有零移到末尾,我们必须找到一个零总是大于任何其他值的变换,但这很容易...... Python 布尔值是有序的,True 更大而不是False,所以我们的魔法转换就是n==0

演示:

In [1]: sorted([0,1,0,3,12], key=lambda n: n==0)
Out[1]: [1, 3, 12, 0, 0]

如果您想避免复制,请使用列表的.sort 方法。

l = [0,1,0,3,12]
l.sort(key=lambda n:n==0)

【讨论】:

经过深思熟虑后,我想我猜想当 l.sort(ky=lambda n: n==0) 在列表 L -> [False, True, False, True, True] -> 所以我们得到了将所有 False('零')移动到末尾的预期结果。非常聪明。 @DanielHao 抱歉,我没有回复您的第 1 条评论(意大利的睡觉时间……),但我看到您说得对!【参考方案3】:

两指针算法

这是时间复杂度为 O(N) 且没有辅助空间的解。

def move_zeros(data: list) -> list:
    size = len(data)
    left = right = 0
    while right < size:
        if data[right] == 0:
            right += 1
        else:
            data[left], data[right] = data[right], data[left]
            right += 1
            left += 1
    return data


if __name__ == "__main__":  
    arr = [1, 2, 0, 0, 3, 4, 0, 0, 0, 7, 2, 4]
    print(move_zeros(data=arr))

输出:

[1, 2, 3, 4, 7, 2, 4, 0, 0, 0, 0, 0]

【讨论】:

【参考方案4】:

list(filter(lambda x: x!= 0, array))+[0]*array.count(0)

[x for x in array if x !=0]+[0]*array.count(0)

【讨论】:

OP 专门要求就地修改列表,因此虽然这看起来是一个很好、简单的解决方案,但它并不能回答问题。【参考方案5】:

您的问题是,在第一步之后,您有两个相邻的零。您交换零,然后将 0 留在 [1] 位置,永远不要回到后面

相反,我建议您迭代数组并保留最后一个非零数 j 的跟踪器,初始化为 -1。您可以将遇到的任何非零值交换为 j+1。之后 0 的顺序无关紧要。

旁注:在 python 中,range(n) 将迭代 0 -> n-1,因此您不需要 and 语句中的第二个条件来终止循环。相反,你可以做 range(n-1),例如

【讨论】:

【参考方案6】:

使用列表推导将列表划分为 2 个列表,然后按所需顺序连接它们:

orig_lst = [0, 1, 0, 3, 12]
lst_non0 = [x for x in orig_lst if x != 0 ]
lst_0    = [x for x in orig_lst if x == 0 ]
lst_0s_at_end = lst_non0 + lst_0
print(lst_0s_at_end)
# [1, 3, 12, 0, 0]

您也可以通过就地更改原始列表来获得相同的结果:

orig_lst = [0, 1, 0, 3, 12]
orig_lst = [x for x in orig_lst if x != 0 ] + [x for x in orig_lst if x == 0 ]

【讨论】:

他说:“我正在尝试就地执行此操作,而不复制数组。”【参考方案7】:

一个人可以只remove 零,同时计算它们,然后在原始列表的剩余部分附加我们之前删除的零...

>>> help(list.remove)
Help on method_descriptor:

remove(self, value, /)
    Remove first occurrence of value.
    
    Raises ValueError if the value is not present.
>>> import random
>>> c, l = 0, [random.randint(0, 10) for _ in range(100)]
>>> while True:
...     try:
...         l.remove(0)
...         c += 1
...     except ValueError:
...          break
>>> l += [0]*c

【讨论】:

这个解决方案将花费二次时间,因为l.remove(0) 需要线性时间,必须移动所有元素。【参考方案8】:

这个想法是使用删除函数删除列表中所有出现的零,并维护一个计数变量来计算零出现的次数,最后多次附加零“计数”。我希望这是一个有效的解决方案对于您的问题。请找到相同的代码:

lis = [1,2,0,5,6,0,6,0,7]
count = 0
for i in lis:
   if(i==0):
      lis.remove(i)
      count = count+1
for i in range(count):
   lis.append(0)
print(lis)

【讨论】:

【参考方案9】:

可以使用python的sorted函数对数组进行排序,保持反转为True。那里也给出了第二种方法。

# one way
array42 = [0, 5, 2,0,0,5,2,7,0,2,3]
print(sorted(array42))


# second way
index = len(array42)-1
for i in range(len(array42)-1,-1,-1):
    if array42[i]!=0:
        array42[index]=array42[i]
        index -= 1

for i in range(index,-1,-1):
    array42[index]=0
    index -= 1
print(array42)

【讨论】:

【参考方案10】:

解决此问题的另一种方法:(向后工作 - 它也可以应用于迭代和处理时的一般情况,通常不推荐)。 时间方面的比较 - @gboffi 更好 O(log N),而这个是 O(N)。


def moveZeros(A):
    for i in range(len(A)-1, -1, -1):
        if not A[i]:       # num. is 0
           del A[i]
           append(0)
      

【讨论】:

这个解决方案是 O(N^2),而不是 O(N),因为每个 del 调用都必须在 A[i] 之后移动所有数据。 @gboffi 的解决方案是 O(N log N),因为它使用排序。 同意。这是演示的一种方法。【参考方案11】:

这个问题有足够的答案,我不知道为什么我觉得有必要再补充一个,但我是。我的解决方案基本上等同于@Cabu,基于您不需要存储您移动的那些 0 的认识,因为它们都是相同的。相反,您可以在数组上循环一次时覆盖它们,并在最后写入正确数量的零。

def moveZeroes(nums) -> None:
    pos = 0  # keep track of where to place the next non-zero value
    for num in nums:
        if num != 0:
            nums[pos] = num
            pos += 1
    for i in range(pos, len(nums)):
        nums[i] = 0

我发布这个额外的解决方案是因为

我更喜欢尽量减少索引的使用:使用 for num in nums: 而不是 for i in range(n):。 我避免在第二个循环中创建一个临时的 0 列表。

因此,此解决方案可能(未经测试,但至少微不足道)更有效:此代码在线性时间内运行并且不需要额外的存储空间。

话虽如此,如果必须保留“零”值,因为它们在某些方面有所不同,我首选的解决方案是@Soumya 提出的双指针算法。它还以线性时间运行并避免任何临时存储。但是,它会打乱那些零,所以如果零的顺序很重要,它就不会按原样工作。

【讨论】:

以上是关于在python中将所有零移动到数组的末尾的主要内容,如果未能解决你的问题,请参考以下文章

将给定数组中的所有零移动到末尾,并将每个非零元素替换为最接近的较大值(如果有)

2021-10-31:移动零。给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。示例:输入: [0,1,0,3,12]。输出: [1,3,12,0,0]。说

Leetcode 数组:283 removezeros 移动零(python)

如何在python中将大于零的多维数组的所有值更改为1? [复制]

领扣(LeetCode)移动零 个人题解

283.移动零