I want to compare every element of the list a with its previous consecutive elements. Each iteration of i in forward direction needs i number of iterations for j in backward direction (Or simply iterate j in reverse from the current index of i) and compare if previous consecutive elements satisfies the condition a[i] >= a[j]. If condition is true increase count by 1 (count ) if condition is fails(i.e a[i] < a[j] at any index) append the count to a new list li[] and print li.
I tried the following way but failed:
a = [1,2,3,2,4,6,1,2]
li=[]
count = 0
for i in range(len(a)):
for j in range(i,0,-1):
while a[i] >= a[j]:
count =1
li.append(count)
print(li)
the output should look like this:
1 2 3 1 5 6 1 2
And is there any specific way I can solve this in complexity O(n). Like is there any algorithm to solve the problems which need 2 or more nested loops in a certain method, to optimize time complexity.
CodePudding user response:
Using numpy you can evaluate comparisons for a whole np.array
. This saves you a loop.
The first step creates arrays containing all indexes with a number greater than tha current value: array([1, 2, 3, 4, 5]
(for index 6 of the original list).
In the second step, empty arrays are added to li
as currenrent index 1 idx 1
since that's how far back the point would have to go (until start of the list).
If the array is non-empty, the difference between idx
and the biggest value (which is the rightmost index in the index
-array) will be added. Again, this is how far the pointer would have to move.
import numpy as np
a = np.array([1, 2, 3, 2, 4, 6, 1, 2])
li = []
for idx, val in enumerate(a):
index = np.where(a[0:idx 1] > val)
if np.size(index) == 0:
li.append(idx 1)
else:
li.append(idx - np.max(index))
print(li)
CodePudding user response:
This is from the category of look-ahead or look-behind problems that require a stack to solve in linear time:
def smaller_preceding(a):
stack = []
result = []
for i, x in enumerate(a):
while stack and a[stack[-1]] < x:
stack.pop()
# current index minus index of last bigger element!
result.append(i - (stack[-1] if stack else -1))
stack.append(i)
return result
>>> smaller_preceding([1,2,3,2,4,6,1,2])
[1, 2, 3, 1, 5, 6, 1, 2]
The stack keeps track of indeces of strictly decreasing elements, e.g. at one point it will be:
[2, 3]
# stack contains indeces (0 and 1 have been popped)
corresponding to the elements:
[3, 2]
# ... of decreasing elements (... because 1 and 2 were smaller than 3)
The reasoning is that for every element x
in a
, you know you can completely forget about all smaller previous elements, as you will never "get past" x
when looking back later on (if any smaller elements are smaller than a future element, so will be x
!).
You can easily verify that the for
-loop has n iterations.
Furthermore, one can see that the while
-loop also has at most n iterations (even though it is nested) because every index can only be popped off the stack once.
Hence, the whole algorithm is linear in time and space.