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]
I'm a beginner in Python. The following is my code for leetcode 221 (Maximal Square), it can only pass more than 30 test samples. It fails at the matrix: M=[["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]].
def maximalSquare(matrix):
colleng=len(matrix)
rowleng=len(matrix[0])
maxsqlst=[]
dp=[[0]*rowleng]*colleng
for i in range(colleng):
for j in range(rowleng):
if matrix[i][j]=='1':
if i==0 or j==0:
print('A',i,j,dp)
dp[i][j]=1
print('B',i,j,dp)
else:
dp[i][j]=min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1
print(i,j,dp)
maxsqlst.append(max(dp[i]))
return max(maxsqlst)**2
By inserting some print() command, I find that it goes wrong when i=j=0,
A 0 0 [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
B 0 0 [[1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0]].
Why does it make the first colume to be 1 instead of just dp[0][0]?
TL;DR: Change your definition of dp from
dp = [[0]*rowleng]*colleng
to
dp = [[0 for i in range(rowleng)] for j in range(colleng)]
Why? In the first, you're creating a 2D list of references to the same list; in the second, you're creating a 2D list of references to unique lists.
(Technically, you could rewrite the 2nd lind as dp = [[0]*rowleng for j in range(colleng)] and get the same result.)
All that to say, when you declare dp to be a list of references to the same list, then when you change dp[0][0], it "also changes" dp[i][0] for all 0 < i < len(dp) -- because it's all the same list being referred to.
See e.g. List of lists changes reflected across sublists unexpectedly for more info.
In your function, you call this line:
dp[i][j]=1
This sets the item of i and j to 1, and if you notice the section where its called:
if matrix[i][j]=='1':
if i==0 or j==0:
You will notice that both of those conditions are true in the cases you get 1, as this value is overriding whatever other values you've had, at least from what I see.
Remember that you are comparing with an or and not an and, so if either i or j were zero, not both necessarily, you will end up overriding the value, especially in locations where your matrix is equal to 1 (Here, your first column is all ones).
This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 1 year ago.
I have a nested list containing nothing but 0's, defined by a size of 5 by 5, and I want to be able to set specific values int he nested list to 1 using direct indexing. This is the solution I currently have:
Grid = [[0] * 5] * 5
ObX = 2
print(ObX)
ObY = 3
print(ObY)
Grid[ObY][ObX] = 1
print(Grid)
Expected output:
2
3
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 0, 0]]
Actual Output:
2
3
[[0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0]]
As far as I can tell, it's ignoring the "ObY" index and just deciding that it wants to replace every value in each list at index "ObX". What am I doing wrong?
The list of lists you created contains the same list 5 times, changing it once changes all of its copies
In [1]: Grid = [[0] * 5] * 5
In [2]: [id(x) for x in Grid]
Out[2]:
[140392398718848,
140392398718848,
140392398718848,
140392398718848,
140392398718848]
Use this to create 5 different lists
In [5]: Grid =[[0]*5 for _ in range(5)]
In [6]: [id(x) for x in Grid]
Out[6]:
[140392397938112,
140392396267776,
140392397478656,
140392398276224,
140392398427200]
I am trying to "map a list of elements to a range of an element from another list to create unique matrices." Let me explain with a drawing.
Kickstart-inspired question
I hope that it makes sense.
This is inspired by Google Kickstart competition, which means that it is not a question exactly required by the contest.
But I thought of this question and I think that it is worth exploring.
But I am stuck with myself and not being able to move on much.
Here is the code I have, which obviously is not a correct solution.
values = input("please enter your input: ")
values = values.split()
values = [int(i) for i in values]
>>> please enter your input: 2 4 3 1 0 0 1 0 1 1 0 0 1 1 0 6 4 1 0 0 0 1 0 0 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 0
rows_columns = []
matrix = []
for i in values:
if i > 1:
rows_columns[:1].append(i) # The "2" at the very beginning indicates how many matrices should be formed
elif i <= 1:
matrix.append(i)
rows_columns[:1]
>>> [4, 3, 6, 4]
matrix_all = []
for i in range(1, len(rows_columns)):
matrix_sub = []
for j in range(rows_columns[i]):
matrix_sub.append(matrix[j])
if matrix_sub not in matrix_all:
matrix_all.append(matrix_sub)
>>> [[1, 0, 0, 1], [1, 0, 0], [1, 0, 0, 1, 0, 1], [1, 0, 0, 1]]
I really wonder if the nested loop is a good idea to solve this question. This is the best way I could think of for the last couple of hours. What I want to get as a final result looks like below.
Final expected output
Given that there is information about how many rows and columns there should be on a matrix on one list and just enough numbers of elements to form the matrix on the other, what would be the solution to map(or create) the two matrices out of the other list, based on the dimensionality information on a list?
I hope that it is clear, let me know when it is not.
Thanks!
Without using numpy, here is one working solution, based on the input found in your code snippet, and the expected result listed in your final expected result link:
values = [2, 4, 3, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 6, 4, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1,
0, 1, 0, 1, 0, 1, 1, 1, 0]
v_idx = 1
"""
As per example, the number of matrices desired is found in the first input list element.
In the above values list, we want 2 matrices. The for loop below therefore executes exactly 2 times
"""
for matrix_nr in range(values[0]):
# The nr of rows and nr of columns are the next two elements in the values list
nr_rows = values[v_idx]
nr_cols = values[v_idx + 1]
# Calculate the start index for the next matrix specifications
new_idx = v_idx+2+(nr_rows*nr_cols)
# Slice the values list to extract the values for the current matrix to format
sub_elements = values[v_idx+2: new_idx]
matrix = []
# Append elements to the matrix by slicing values according to nr_rows and nr_cols
for r in range(nr_rows):
start_idx = r*nr_cols
end_idx = (r+1)*nr_cols
matrix.append(sub_elements[start_idx:end_idx])
print(matrix)
v_idx = new_idx
This gives the expected result:
[[1, 0, 0], [1, 0, 1], [1, 0, 0], [1, 1, 0]]
[[1, 0, 0, 0], [1, 0, 0, 1], [1, 1, 1, 1], [1, 0, 1, 0], [1, 0, 1, 0], [1, 1, 1, 0]]
As said, numpy could very likely be used to be a lot more efficient.
I have an numpy array like this:
a = np.array([[1, 0, 1, 1, 1],
[1, 1, 1, 1, 0],
[1, 0, 0, 1, 1],
[1, 0, 1, 0, 1]])
Question 1:
As shown in the title, I want to replace all elements with zero after the first zero appeared. The result should be like this :
a = np.array([[1, 0, 0, 0, 0],
[1, 1, 1, 1, 0],
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 0]])
Question 2: how to slice different columns for each row like this example?
As I am dealing with an array with large size. If any one could find an efficient way to solve this please. Thank you very much.
One way to accomplish question 1 is to use numpy.cumprod
>>> np.cumprod(a, axis=1)
array([[1, 0, 0, 0, 0],
[1, 1, 1, 1, 0],
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 0]])
Question 1:
You could iterate over the array like so:
for i in range(a.shape[0]):
j = 0
row = a[i]
while row[j]>0:
j += 1
row[j+1:] = 0
This will change the array in-place. If you are interested in very high performance, the answers to this question could be of use to find the first zero faster. np.where scans the entire array for this and therefore is not optimal for the task.
Actually, the fastest solution will depend a bit on the distribution of your array entries: If there are many floats in there and rarely is there ever a zero, the while loops in the code above will interrupt late on average, requiring to write only "a few" zeros. If however there are only two possible entries like in your sample array and these occur with a similar probability (i.e. ~50%), there would be a lot of zeros to be written to a, and the following will be faster:
b = np.zeros(a.shape)
for i in range(a.shape[0]):
j = 0
a_row = a[i]
b_row = b[i]
while a_row[j]>0:
b_row[j] = a_row[j]
j += 1
Question 2:
If you mean to slice each row individually on a similar criterion dealing with a first occurence of some kind, you could simply adapt this iteration pattern. If the criterion is more global (like finding the maximum of the row, for example) built-in methods like np.where exist that will be more efficient, but it probably would depend a bit on the criterion itself which choice is best.
Question 1: An efficient way to do this would be the following.
import numpy as np
a = np.array([[1, 0, 1, 1, 1],
[1, 1, 1, 1, 0],
[1, 0, 0, 1, 1],
[1, 0, 1, 0, 1]])
for row in a:
zeros = np.where(row == 0)[0]
if (len(zeros)):# Check if zero exists
row[zeros[0]:] = 0
print(a)
Output:
[[1 0 0 0 0]
[1 1 1 1 0]
[1 0 0 0 0]
[1 0 0 0 0]]
Question 2: Using the same array, for each row rowIdx, you can have a array of columns colIdxs that you want to extract from.
rowIdx = 2
colIdxs = [1, 3, 4]
print(a[rowIdx, colIdxs])
Output:
[0 1 1]
I prefer Ayrat's creative answer for the first question, but if you need to slice different columns for different rows in large size, this could help you:
indexer = tuple(np.s_[i:a.shape[1]] for i in (a==0).argmax(axis=1))
for i,j in enumerate(indexer):
a[i,j]=0
indexer:
(slice(1, 5, None), slice(4, 5, None), slice(1, 5, None), slice(1, 5, None))
or:
indexer = (a==0).argmax(axis=1)
for i in range(a.shape[0]):
a[i,indexer[i]:]=0
indexer:
[1 4 1 1]
output:
[[1 0 0 0 0]
[1 1 1 1 0]
[1 0 0 0 0]
[1 0 0 0 0]]