2

So the goal here is to move all 0's to the end of the array but I stumble across a problem from these small lines of code.

When I use the input I get the desired output like so:

Input: [0,1,0,3,12]
Output: [1,3,12,0,0]

However, whenever I use this input:

[0,0,1]

I get this output:

[0,1,0]

When I want the output:

[1,0,0]

I have no idea why I thought I implemented this properly:

class Solution:
def moveZeroes(self, nums):
    """
    :type nums: List[int]
    :rtype: void Do not return anything, modify nums in-place instead.
    """
    for counter in range(len(nums)):
        if nums[counter] == 0:
            nums.pop(counter) #Remove the 0
            nums.append(0) #Add to end. 
            counter-=1 #Check same index just incase adjacent 0

Any input is appreciated. Thanks!

Michael Gee
  • 501
  • 2
  • 9
  • 21
  • there is an easier way to this: `x = [1,5,6,0,4]` and than sort your list: `x.sort(reverse = True)` – J.A.Cado May 22 '18 at 06:28
  • @J.A.Cado That's not the same thing. It'll sort all values, not just shunt the zeroes to the end. – AKX May 22 '18 at 06:29

3 Answers3

5

I wouldn't even bother with a manual for loop...

def move_zeroes(nums):
    nums[:] = [n for n in nums if n != 0] + [0] * nums.count(0)

x = [0,1,0,3,12]
move_zeroes(x)
print(x)

outputs

[1, 3, 12, 0, 0]
AKX
  • 152,115
  • 15
  • 115
  • 172
4

A for loop is not a equivalent to a while loop with increment. Your last line counter-=1 doesn't achieve anything: after you decrement counter, it immediately takes the next value in the range. (To be clear, if you were at iteration counter = 2 for example, no matter what value you leave counter at at the end of this iteration, it will be reassigned to 3 at the next iteration.)

Julien
  • 13,986
  • 5
  • 29
  • 53
3

This line:

counter-=1 #Check same index just incase adjacent 0

Does not decrease the indexing for the following indices.

Try this instead:

class Solution:
    def moveZeroes(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        zeros_found = 0
        for counter in range(len(nums)):
            idx = counter - zeros_found
            if nums[idx] == 0:
                nums.pop(idx) #Remove the 0
                nums.append(0) #Add to end. 
                zeros_found += 1 #Account for offset

You could also do this more functionally:

from itertools import filterfalse
# ...
def moveZeroes(self, nums):
    none_zeroes = list(filter(None, nums))
    zeros = list(filterfalse(None, nums))
    return none_zeros + zeros

Or if you don't want to create lists for no reason:

from itertools import filterfalse, chain
# ...
def moveZeroes(self, nums):
    none_zeroes = filter(None, nums)
    zeros = filterfalse(None, nums)
    return list(chain(none_zeros, zeros))

These rely on two implicit facts:

  1. bool(0) is False, and bool(x) when x != 0 is True
  2. filter(None, list) filters list by item's bool value, so no 0s (and opposite for filterfalse)
Reut Sharabani
  • 30,449
  • 6
  • 70
  • 88