I'm working on the problem of moving all the zeros in the array to the end.
I know this has been asked before but I'm not looking for the solution - my problem is that I can't think in a logical way to stop the loop because I implemented it on Python and it loops forever.
This is what I have:
def move_zeros(array):
i=0
while i<len(array):
if array[i] == 0:
for j in range(i, len(array)-1):
array[j] = array[j+1]
array[len(array)-1] = 0
else:
i+=1
return array
The only thing I can think right now is to add a counter outside the if-else and use break when the counter is equal to the length of the array, like this:
def move_zeros(array):
i=0
count = 0
while i<len(array):
if array[i] == 0:
for j in range(i, len(array)-1):
array[j] = array[j+1]
array[len(array)-1] = 0
else:
i+=1
count += 1
if count == len(array):
break
return array
But I know this is cheating in some way because I used python and knew it was going on forever, and I don't want to search for the problem online because I don't want to see the full solution... Any help? Thank you.
Conceptually, imagine that instead of putting the zeroes to the end of the array, you just remove them, shifting everything else back. In that case, when you don't find a zero, you still increment i by 1, but when you do find a zero, the "array length" also gets smaller by 1. Let's define a new variable limit which keeps track of this imaginary length; the zeroes will still get swapped to the end of the array, but the loop ends when i reaches this limit (because the rest of the array is all the zeroes you "removed" on earlier iterations).
def move_zeros(array):
i = 0
limit = len(array)
while i < limit:
if array[i] == 0:
for j in range(i, limit - 1):
array[j] = array[j + 1]
array[limit - 1] = 0
limit -= 1
else:
i += 1
return array
This loop provably terminates in len(array) iterations, because on each iteration either i increases or limit decreases, making them closer together by 1 and eventually the loop terminates because they become equal.
Related
my code is working mostly fine except for a small bug where it is placing the second element at the beginning no matter the value e.g. 1,6,2,9,3,8,4,7,5,0 would become 6,0,1,2,3,4,5,7,8,9
I've tried altering the numbers in the FOR loop a bit but other than that genuinely cannot see what's wrong
def InsertionSort(array):
for i in range(len(array)):
for j in range(len(array)):
if array[i] <= array[j+1]:
new = array.pop(i)
array.insert(j+1,new)
break
elif array[i] > array[j+1]:
continue
print (array)
The problem there is, that you switch the position of the second element with the first, but later on not compare it anymore.
Here an implementation which is close to yours, with slight changes to take care of all the entries
ar =[1,6,2,9,3,8,4,7,5,0]
def InsertionSort(array):
for i in range(len(array)):
val = array[i]
while( i > 0 and array[i-1] > val):
array[i] = array[i-1]
i = i - 1
array[i] = val
print (array)
InsertionSort(ar)
Any time you modify the collection you are looping over, you can get different behavior than what you expect. This is especially true when you are removing items from the collection since it messes with the current index.
Here is an example that simply swaps the orders of elements (and also prints debug information along the way so you can see exactly what is happening):
def insertionSort(my_list):
# for every element in our array
for index in range(1, len(my_list)):
current = my_list[index]
position = index
while position > 0 and my_list[position-1] > current:
print("Swapped {} for {}".format(my_list[position], my_list[position-1]))
my_list[position] = my_list[position-1]
print(my_list)
position -= 1
my_list[position] = current
return my_list
my_list = [8,2,1,3,5,4]
print(insertionSort(my_list))
I am trying to implement the code below, and I'm getting an error of
"index is out of range". I think I am getting the error because the for loop saved the value of the length of the array, while I changed it inside the loop itself.
I can't figure out how to solve it using a for loop.
I did solve it using a recursive way, but it was computationally expensive and I am dealing with billions of words. BTW, the Comp function only returns a pool if the 2 words having the same sentence, so I believe it doesn't effect.
I am using Python-3, pycharm.
def reg(array,n):
f=open(r"C:\Users\Ali\Desktop\love.txt","w")
length= len(array)
if length==1:
return array
k=0
for item in range (length-1):
k+=1
for j in range(k,length):
if Comp(array[item][0],array[j][0])>=n:
f.write(str("\n"))
f.write(str(array[item][1]))
f.write(str("\n"))
f.write(str(array[j+k ][1]))
array[k+j]=array.pop()
length-=1
break
f.close()
pass
Instead of modifying the array, just keep track of your effective last index and break out when you hit it. Something like (untested)
k = 0
last_index = len(array) - 1
for item in range(length - 1):
if item > last_index:
break
k += 1
# ....
array[k+j] = array[last_index]
last_index -= 1
break
You are given an unordered array consisting of consecutive integers [1, 2, 3, ..., n] without any duplicates. You are allowed to swap any two elements. You need to find the minimum number of swaps required to sort the array in ascending order. The code what i have got ,timed out for a few test cases. Is there any way that we can optimise the code?
My code is as follows:
def minimumSwaps(arr):
lst=[ele+1 for ele in range(len(arr))]
cnt=0
for i in range(len(arr)):
if(lst[i]!=arr[i]):
k=arr.index(lst[i])
arr[i],arr[k]=arr[k],arr[i]
cnt=cnt+1
return cnt
You don't really need to generate a reference list lst because you should know that n in arr should be in the index n-1 to be ascending. Also, doing k=arr.index(lst[i]) requires O(n) time to search, and which is complete unnecessary. Here's my solution:
def minimumSwaps(arr):
total_swaps = 0
start = 0
while start < len(arr):
if arr[start] == start + 1:
start += 1
continue
arr[arr[start] - 1], arr[start] = arr[start], arr[arr[start] - 1]
total_swaps += 1
return total_swaps
If I guess it right, it's a question on Hackerrank, and this was my solution by the time passed the tests. :P
This algorithm starts at position one and swaps the items under the cursor directly into their correct positions. When the correct item is swapped into this position it increments the cursor to the next item and repeats the process.
This is more efficient than stepping through the array sequentially, swapping the correct item into each position, because you don't have to work out exactly where the correct item is located within the tail of the list.
I used the logic given in link here: https://www.youtube.com/watch?v=Def9kMtZCNA
def minimumSwaps(arr):
# element usually at element value-1 index value
# eg, 1 at index 0, 2 at index 1.....
counter=0
for i in range(len(arr)):
if arr[i] != i+1:
while arr[i] != i+1:
temp=arr[arr[i]-1]
arr[arr[i]-1]=arr[i]
arr[i]=temp
counter+=1
else:
continue
return counter
rmNegative(L) removes the negative numbers from list L, assumed to contain only numeric elements. (Modifies L; does not create a new list.)
How would I go about this when using a while loop? I've tried coding it over and over and all I got was a never ending loop..
def rmNegatives(L):
pos=len(L)-1
while pos>-1:
pos=pos
if pos<len(L)-1:
pos=pos
if L[pos]>0:
L[:]=L[:]
pos=len(L)-2
elif L[pos]<0:
L[:]=[L[pos]]+L[0:]
L[:]=L[1:]
pos=len(L)-1
elif pos==len(L)-1:
pos=pos
if L[pos]<0:
L[0:]=L[0:pos]
pos=len(L)-1
elif L[pos]>0:
L[:]=L[:]
pos=len(L)-2
rmNegatives([-25,31,-10,23,45,-2])
Run the code here
edit** i thank you for your responses. the reason why my code does not contain any form of remove or index is because i was restricted from using them(would have been nice if they weren't but..)
Here is an implementation with a while loop. I start at the end because the list is getting shorter as the loop iterates and the earlier indexes are shifting as a result.
def rmNegative(L):
index = len(L) - 1
while index >= 0:
if L[index] < 0:
del L[index]
index = index - 1
If you absolutely cannot afford to create a new list and you have to use a while loop:
l = [1,2,3,-1,-2,-3,4,5]
x = 0
while x < len(l):
if l[x] < 0:
l.remove(l[x])
continue
x += 1
Alternatively, as suggested by abarnert (lower runtime):
l = [1,2,3,-1,-2,-3,4,5]
x = 0
while x < len(l):
if l[x] < 0:
del l[x]
continue
x += 1
If you absolutely cannot afford to create a new list, but can use a for loop:
l = [1,2,3,-1,-2,-3,4,5]
for x in xrange(len(l)):
if x < len(l) and l[x] < 0:
l.remove(l[x])
If you can afford to create a new list:
l = [1,2,3,-1,-2,-3,4,5]
l = [num for num in l if num >= 0]
Code:
def rmNegatives(L):
i = 0
while i < len(L):
if L[i] < 0:
L.pop(i)
else:
i += 1
return L
The first think you need to know is that del L[i] removes the ith element. (Don't use L.remove(L[i]); that looks up the ith element, then searches the whole list until it finds an equal value, then deletes that.)
But notice that if you delete L[3], and then move on to L[4], you've skipped one value—the original L[4] is now L[3], and you still need to check it. So you have to make sure not to increment i until you find a value that you're keeping.
But if you just loop over all of the indices, and you've deleted some along the way, you're going to run off the end. So, you need to decrement the the length every time you delete. Or you could also just keep calling len(L) again and checking against the new length each time.
One clever way to solve both of those problems at once is to count backward, as in Brad Budlong's answer. But that can make it easy to make fencepost errors. So, I'll do it the other way.
def rmNegative(L):
length = len(L)
i = 0
while i < length:
if L[i] < 0:
del L[i]
length -= 1
else:
i += 1
And that's it.
If you want to know why your existing code is an infinite loop, let's step through a bit of what it does.
We start with pos=len(L)-1, so we go into the big elif. pos=pos does nothing. If it's a negative number, you remove it and go to the new len(L)-1; if it's a positive number, you leave it, and go to len(L)-2. If it's 0, we do neither, which means pos=len(L)-1 still, and we're just going to repeatedly look at that 0 forever.
So, that's one way to get an infinite loop. But let's assume it didn't end in 0.
If we've just removed a negative number, we go back to the elif, and we know that's OK unless 0.
But if we've left a positive number, then we now have pos=len(L)-2, so we go to the if. Again, pos=pos does nothing. If it's a positive number, we copy the list to itself, which does nothing, then set pos=len(L)-2. Which is the same thing it already is. So, if the next-to-last number is positive, we're just going to keep looking at that number forever. That's another way to get an infinite loop.
What if it was negative? Then L[:]=[L[pos]]+L[0:] prepends the value you wanted to delete to the whole list (which still includes the original copy of the value), L[:]=L[1:] removes the value you just prepended, so you end up with the same values in L you started with. Then you set pos=len(L)-1, which goes back to the end of the list. We know that's going to work successfully, and get back to the next-to-last slot again, which will still be the same value, so we'll shuttle back and forth forever. So, that's enough way to get an infinite loop.
What if it was 0? Then we do nothing, so pos and L never change, so that's another way to get an infinite loop.
So, once we get a positive element on the end of the list, all three possibilities for the next-to-last element are infinite loops.
Stepping back a bit, the only things your code ever sets pos to are len(L)-1 and len(L)-2. So, even if you did all of the others stuff right, how could this possibly ever complete on a list with more than 2 non-negative numbers?
Here is my implementation of rmNegative(L).
def rmNegative(L):
m = min(L)
while m < 0:
L.remove(m)
m = min(L)
This modifies the list and does not create a new list.
Well, not allocating new list now,
>>> list_l = [1, -1, 2, -8]
>>> length = len(list_l)
>>> while i < length:
... if list_l[i] < 0:
... del list_l[i]
... length -= 1
... else:
... i = i + 1
...
>>> list_l
[1, 2]
I attempted to implement a merge sort, here is my code:
def mergeSort(array):
result=[]
n=len(array)
if n==1:
result=array
else:
a=round(n/2)
first=mergeSort(array[0:a])
second=mergeSort(array[a:n])
for i in range(len(first)):
for j in range(len(second)):
if first[i]<second[j]:
result.append(first[i])
i=i+1
else:
result.append(second[j])
j=j+1
return result
a=[5,4,1,8,7,6,2,3]
b=mergeSort(a)
print(b)
Unfortunately, the result turns out to be [1]. What is wrong with my function?
A number of things...
Firstly, this is a recursive function, meaning you cannot create a list within the function, as you did here:
result=[]
This will simply reset your list after every recursive call, skewing your results. The easiest thing to do is to alter the list that is passed as a parameter to merge sort.
Your next problem is that you have a for loop within a for loop. This will not work because while the first for loop iterates over first, the second for loop will iterate over second for every increment of i, which is not what you want. What you need is to compare both first and second and extract the minimum value, and then the next minimum value, and so on until you get a sorted list.
So your for loops need to be changed to the following:
while i < len(first) and j < len(second):
Which leads me to final problem in your code. The while loop will exit after one of the conditions are met, meaning either i or j (one or the other) will not have reached len(first) or len(second). In other words, there will be one value in either first or second that is unaccounted for. You need to add this unaccounted value to your sorted list, meaning you must implement this final excerpt at the end of your function:
remaining = first if i < j else second
r = i if remaining == first else j
while r < len(remaining):
array[k] = remaining[r]
r = r + 1
k = k + 1
Here r represents the index value where the previous while loop broke off. The while loop will then iterate through the rest of the remaining values; adding them to the end of your sorted list.
You merge sort should now look as follows:
def mergeSort(array):
if len(array)==1:
return array
else:
a=round(len(array)/2)
first=mergeSort(array[:a])
second=mergeSort(array[a:])
i = 0
j = 0
k = 0
while i < len(first) and j < len(second):
if first[i]<second[j]:
array[k] = first[i]
i=i+1
k=k+1
else:
array[k] = second[j]
j=j+1
k=k+1
remaining = first if i < j else second
r = i if remaining == first else j
while r < len(remaining):
array[k] = remaining[r]
r += 1; k += 1
return array
I tried to not alter your code as much as possible in order to make it easier for you to understand. However, if your difficulty in understanding what I did persists, try de-bugging your merge sort using multiple print statements so that you can follow the function's progress and see where it goes wrong.