Related
I'm solving a simple DSA problem and seem to grasp a general way to solve the question where Given an integer array called nums, I would move all 0's to the end of it while maintaining the relative order of the non-zero elements.
For example, nums = [0,1,0,3,12]
Then the expected output would be. [1,3,12,0,0]
My approach was as below.
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
count = 0
while count < len(nums):
if len(nums[count+1:]) == count:
return nums
elif nums[count] == 0:
nums.pop(nums[count])
nums.append(0)
else:
count += 1
print(nums)
I would expect this to work in my mind, but the output is not what I expected because stdout shows as
[1, 0, 3, 12, 0]
[1, 0, 3, 12, 0]
[0, 3, 12, 0, 0]
[0, 3, 12, 0, 0]
Now I understand why the firsto two prints show as such, because 0 was identifed in the first loop and popped
However I do not understand why in the third and forth iteration, the result shows as [0,3,12,0,0].
The if-conditiona says if nums[count]==0, then nums.pop[nums[count]].
How is the pop and append triggered when the condition is not met?
Am I missing something here?
I simplified in few lines:
def ceros_array(ar):
'''Given and array of integer return zeros at end of array'''
zeros = ar.count(0) # count zeros in array
new_array = [x for x in ar if x !=0] # new array with elements different that zero
for i in range(zeros): # cycle for zeros removed
new_array.append(0) # insert 0 at end of new array
return new_array
I tested with these arrays:
>>> ceros_array([1, 0, 3, 12, 0])
[1, 3, 12, 0, 0]
>>> ceros_array([0, 0, 3, 12, 0])
[3, 12, 0, 0, 0]
>>> ceros_array([0, 0, 3, 0, 0])
[3, 0, 0, 0, 0]
>>> ceros_array([0, 0, 0, 0, 1])
[1, 0, 0, 0, 0]
>>> ceros_array([0, 0, 0, 0, 0])
[0, 0, 0, 0, 0]
Inspired by dannisis's answer:
def shift1(nums: list[int]) -> list[int]:
"""Push all zeros in nums to the back (right) of the list."""
# Keep only non-zeroes
new_nums = [x for x in nums if x != 0]
# Append the correct number of zeroes
new_nums += [0] * nums.count(0)
return new_nums
for nums in [
[1, 0, 3, 12, 0],
[0, 0, 3, 12, 0],
[0, 0, 3, 0, 0],
[0, 0, 0, 0, 1],
[0, 0, 0, 0, 0],
]:
print(shift1(nums))
[1, 3, 12, 0, 0]
[3, 12, 0, 0, 0]
[3, 0, 0, 0, 0]
[1, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
Here's a more general, albeit less performant, solution:
def shift2(nums: list[int]):
_len = len(nums)
shift_ct = 0
for i in range(_len):
if nums[i] == 0:
shift_ct += 1
continue
else:
nums[i - shift_ct] = nums[i]
# "Back fill" end w/any zeroes
i = _len - shift_ct
while i < _len:
nums[i] = 0
i += 1
This mutates the list you pass in:
Nums = list[int] # input to function
Want = list[int] # what we expect in return
test_cases: list[tuple[Nums, Want]] = [
([0, 1], [1, 0]),
([0, 0, 1], [1, 0, 0]),
([0, 1, 2], [1, 2, 0]),
([0, 1, 0, 2], [1, 2, 0, 0]),
([0, 1, 0, 2, 0, 3], [1, 2, 3, 0, 0, 0]),
]
def test_shift2():
for nums, want in test_cases:
got = nums[:] # make copy to keep "input" separate from "result" in print-out, if test fails
shift2(got)
if got != want:
print(f"shift2({nums}) = {got}; want {want}")
I've run a number of timeit tests and profiled both, and I believe shift1 is slightly faster because even though it has two function calls (1 for the list comprehension, 1 for count()), it just has less lines to execute; shift2 only has the one function call (len()), but far more lines for the interpreter to step through.
You need to be very careful about changing structure of iterable objects WHILE iterating through them. Think about what you are doing. On your first loop, you hit 0 when i = 1, you pop that out and append it to the end. So now, all the elements from that point are shuffled up to an index that is one lower. The 2nd 0 was initially at index 2 but is shuffled up to index 1. You then ADD 1 to your index, meaning the next element is skipped and not processed.
Your best solution is to is to append non-zero elements to a second list and then fill with zeros:
def moveZeroes(nums) -> None:
new_list = []
zero_count = 0
for i in nums:
if i > 0:
new_list.append(i)
else:
zero_count += 1
new_list += [0] * zero_count
print(new_list)
moveZeroes([0, 1, 0, 3, 12])
I tested this and this works. (note that I used pop(count), not pop(nums[count]):
NOTE: After posting this answer, I noticed that it would fail if the initial list (my_numbers) starts with more than one zero. A quick and dirty fix for this would be to just call moveZeroes(my_numbers) twice.
def moveZeroes(nums):
count = 0
while count < len(nums):
if nums[count] == 0:
nums.pop(count)
nums.append(0)
count += 1
else:
count += 1
my_numbers = [0,1,0,2,12,0,4]
moveZeroes(my_numbers)
moveZeroes(my_numbers) # added as temporary fix for leading multiple zeroes
print(my_numbers) #prints [1,2,12,4,0,0,0]
For this given list:
a = [1, 0, 0, 1, 1, 1, 3, 0,
1, 1, 4, 2, 1, 1, 2, 1, 1, 1, 1,
0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 5]
I would like to split the list into sublists of zeros and nonzero values to have the desired output of
a = [[1], [0,0], [1,1,1,3], [0],
[1,1,3,2,1,1,2,1,1,1,1,],
[0], [1], [0], [1,1,1], [0,0,0], [1,5]]
I know how to split a list into a certain length of sublists, but I don't think I can use that method here...
You can use itertools.groupby
from itertools import groupby
a = [list(g) for k, g in groupby(a, lambda x:x>0)]
groupby(a, lambda x:x>0) groups successive 0 values or non-zero values together.
Above only handles non-negative values. Improvement from FreddyMcloughlan comment to handle all integers.
a = [list(g) for k, g in groupby(a, lambda x:x!=0)]
You can just iterate over each value, and keep a buffer c and append the buffer to b when the element changes from 0 to a number > 0:
b = []
c = [a[0]]
for i in a[1:]:
if (c[-1] > 0 and i > 0) or (c[-1] == 0 and i == 0):
# Continue run
c.append(i)
else:
b.append(c)
c = [i]
b.append(c)
i.e. The conditional checks to see if the buffer c has changed from a non-zero array to a > 0 array (and vice versa) and if it has not changed, you extend the buffer. When it has changed, append c to b (where b is the final array) then change the buffer to the next value i.
Edit
Your example only includes positive and zero values. If by "nonzero" you are including negative numbers with positive numbers, you can use:
b = []
c = [a[0]]
for i in a[1:]:
# Use != 0, rather than > 0
if (c[-1] != 0 and i != 0) or (c[-1] == 0 and i == 0):
# Continue run
c.append(i)
else:
b.append(c)
c = [i]
b.append(c)
# Negative values:
>>> a = [1, 0, 0, 1, -1, 1, 3, 0,
... 1, 1, 4, 2, 1, 1, 2, 1, 1, 1, 1,
... 0, 1, 0, 1, 1, 1, 0, 0, 0, -1, 5]
# Run algorithm above
>>> b
[[1], [0, 0], [1, -1, 1, 3], [0],
[1, 1, 4, 2, 1, 1, 2, 1, 1, 1, 1],
[0], [1], [0], [1, 1, 1], [0, 0, 0], [-1, 5]]
I'm trying to produce all permutations of a certain number of numbers (for example, 0s and 1s) for a variable number of positions. I will call the number of numbers ord (e.g. ord=2 for only 0s and 1s; ord=3 for 0s, 1s, and 2s) and the number of positions Num. Hence the number of permutations is ord**Num.
Note: I don't want to use itertools or any other types of built-in functions. I'm asking this out of curiosity, not just trying to find a solution.
For ord=2 and Num=3, the output, in any order, should be:
[[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]]
This can be accomplished by:
ord = 2
mylist = []
for a in range(ord):
for b in range(ord):
for c in range(ord):
mylist.append([a,b,c])
For ord = 2 and Num = 4, the output should be:
[[0,0,0,0],[0,0,0,1],[0,0,1,0],[0,0,1,1],[0,1,0,0],[0,1,0,1],[0,1,1,0],[0,1,1,1],[1,0,0,0],[1,0,0,1],[1,0,1,0],[1,0,1,1],[1,1,0,0],[1,1,0,1],[1,1,1,0],[1,1,1,1]]
But then I would have to add another nested for loop:
ord = 2
mylist = []
for a in range(ord):
for b in range(ord):
for c in range(ord):
for d in range(ord):
mylist.append([a,b,c,d])
An obvious solution is to add 0s and 1s randomly to a list of length Num and then to accept that list if it hasn't already been added to mylist, but I want a solution that isn't quite so ridiculous.
This is the closest I've gotten so far to a real solution:
def myperms(elem, mylist):
for i in range(len(elem)-1,-1,-1):
while (elem[i] + 1) < ord:
elem = list(elem)
elem[i] += 1
if elem not in mylist:
mylist.append(elem)
if (elem[i] + 1) >= ord:
elem = list(elem)
elem[i] = 0
return mylist
Num = 3
ord = 2
TotsNum = ord**Num
mylist = []
elem = [0,]*Num
mylist.append(elem)
print(myperms(elem, mylist))
But this only gives:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [1, 0, 0]]
I've tried calling the function within itself (recursion), but I haven't been able to figure out how to do it properly. Does anyone have any ideas about how to solve it recursively? Thank you!
Let's use a recursive solution:
def get_seq(ord, num):
val = [0]*num
N = num
def recurse(ord, num):
for i in range(ord):
val[N - num] = i
if num > 1:
yield from recurse(ord, num-1)
else:
yield val[:]
return recurse(ord, num)
print(list(get_seq(2, 4)))
Output:
[[0, 0, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 0],
[0, 0, 1, 1],
[0, 1, 0, 0],
[0, 1, 0, 1],
[0, 1, 1, 0],
[0, 1, 1, 1],
[1, 0, 0, 0],
[1, 0, 0, 1],
[1, 0, 1, 0],
[1, 0, 1, 1],
[1, 1, 0, 0],
[1, 1, 0, 1],
[1, 1, 1, 0],
[1, 1, 1, 1]]
For other inputs:
>>> list(get_seq(3, 2))
[[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2], [2, 0], [2, 1], [2, 2]]
Use binary, which consists of 0s and 1s.
n = 4
all_permutations = []
for i in range(2**n):
permutation = []
string = bin(i)
string = string.split("0b")[1]
while len(string) != n:
string = f"0{string}"
for i in string:
permutation.append(int(i))
all_permutations.append(permutation)
print(all_permutations)
im trying to write a nonogram program and having a trouble with understanding the following:
I'm trying to create a nested list using recursion from the following parameters:
row: a list made of 1, 0 or -1, eg. [0,-1,-1,1]
lst: an empty lst that will be built as an single option
i: the index the recursion will run with
*final: the final list of lists that will be built
1 and 0 in the original list have to remain unchanged and -1 has to be changed to 0 or a 1.
i have to return a list of list with representing all the options that can be assembled from the original list
here are some examples -
input: `[-1,1,1,-1,1]`
output: `[[1, 1, 1, 1, 1], [1, 1, 1, 0, 1], [0, 1, 1, 1, 1], [0, 1, 1, 0, 1]]`
input:`[-1,-1,-1]`
output:`[[1, 1, 1], [1, 1, 0], [1, 0, 1], [1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 0, 1], [0, 0, 0]]`
input:`[0,-1,-1]`
output: `[[0, 1, 1], [0, 1, 0], [0, 0, 1], [0, 0, 0]]`
this code does work for situations like [-1,-1,-1] but does not work for input like the first expample. it chnages 0 and 1 from the original list but it shouldnt
BLACK = 1
WHITE = 0
UNKNOWN = -1
def row_helper(row, lst, i, final):
if len(lst) == len(row):
copied = copy.deepcopy(lst)
final.append(copied)
return
if row[i] == BLACK or row[i] == WHITE:
lst.append(row[i])
row_helper(row, lst, i + 1, final)
else:
lst.append(BLACK)
row_helper(row, lst, i + 1, final)
lst.pop()
lst.append(WHITE)
row_helper(row, lst, i + 1, final)
lst.pop()
final1 = []
j = 0
lst2 = []
row1 = [1, 0, -1, -1]
row_helper(row1,lst2, j, final1)
i tried replacing the list editing with tuple and than converting to a list later and this code does work for every situation but im trying to figure out how to fix the first code without using tuples
this is the one that work:
def row_helper(row, temp_tup, i, valid_options):
if i == len(row):
painted_row = [block for block in temp_tup]
valid_options.append(painted_row)
return
if row[i] == BLACK or row[i] == WHITE:
temp_tup = temp_tup + (row[i],)
row_helper(row, temp_tup, i + 1, valid_options)
else:
temp_tup = temp_tup + (BLACK,)
row_helper(row, temp_tup, i + 1, valid_options)
temp_tup = temp_tup[:-1]
temp_tup = temp_tup + (WHITE,)
row_helper(row, temp_tup, i + 1, valid_options)
final1 = []
j = 0
lst2 = tuple()
row1 = [0,-1,-1]
row_helper(row1, lst2, j, final1)
print(final1)
help would be greatly appriciated
My matrix is a 8x8 having binary values. I want to filter out patterns of consecutive three 1's i.e.(111) in the diagonals of upper triangular matrix of M. I have written a piece of python code with for and while loop but it did not work and I am unable to figure out whats happening there. Please help..
rf =([1,0,1,0,1,0,0,0],
[1,0,1,0,1,0,0,0],
[1,0,1,0,1,0,0,0],
[1,0,1,0,1,0,0,0],
[1,0,1,0,1,0,0,0],
[1,0,1,0,1,0,0,0],
[1,0,1,0,1,0,0,0],
[1,0,1,0,1,0,0,0])
for i in range(1):
for j in range(len (rf)-3):
while (i<len(rf)-3 and j<len(rf)-3):
count =0
if rf[i,j]==True:
for w in range(3):
if rf[i+w,j+w]==True:
count +=1
print count
if count==3:
i=i+3
j=j+3
else:
rf[i,j]=False
i=i+1
j=j+1
You might simplify your code using numpy to access diagonals:
>>> import numpy as np
>>> rf = [[1,0,1,0,1,0,0,0]] * 8
>>> m = np.array(rf)
>>> m.diagonal(0)
array([1, 0, 1, 0, 1, 0, 0, 0])
>>> m.diagonal(1)
array([0, 1, 0, 1, 0, 0, 0])
a simply routine to find positions of consecutive ones:
def consecutive_values(arr, val=1, cnt=3):
def comparator(pos):
return arr[pos] == val
if len < cnt:
return []
else:
return [p for p, x in enumerate(arr[:1-cnt])
if all(map(comparator, xrange(p, p+cnt, 1)))]
and usage:
>>> consecutive_values([1]*5)
[0, 1, 2]
>>> consecutive_values([1]*5 + [0]*4 + [1]*3)
[0, 1, 2, 9]
>>> m = np.array([[1]*8]*8)
>>> diagonals = map(m.diagonal, range(len(m)))
>>> map(consecutive_values, diagonals)
[[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4], [0, 1, 2, 3], [0, 1, 2], [0, 1], [0], [], []]