Leetcode question #845 Longest Mountain in Array - python

Here is my code:
def longestMountain(self, A: List[int]) -> int:
i=1
list=[]
if len(A)<3:
return 0
if (len(A)==3 and A[0]>=A[1] and A[1]<=A[2]):
return 0
while i < len(A):
count=1
if A[i]<=A[i-1]:
i+=1
while (i< len(A) and A[i]>A[i-1]):
count+=1
i+=1
if count==1 or count==len(A):
return 0
while (i< len(A) and A[i]<A[i-1]):
count+=1
i+=1
list.append(count)
return max(list)
When testing the case which is A=[0,1,0,2,2], the expected result is 3 but output 0. Can someone check my code?
Here is the leetcode question:
Let's call any (contiguous) subarray B (of A) a mountain if the following properties hold:
B.length >= 3
There exists some 0 < i < B.length - 1 such that B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]
(Note that B could be any subarray of A, including the entire array A.)
Given an array A of integers, return the length of the longest mountain.
Return 0 if there is no mountain.
Example 1:
Input: [2,1,4,7,3,2,5]
Output: 5
Explanation: The largest mountain is [1,4,7,3,2] which has length 5.
Example 2:
Input: [2,2,2]
Output: 0
Explanation: There is no mountain.

We can just count the ups and downs and use max():
class Solution:
def longestMountain(self, nums):
longest = 0
up = down = 0
for i in range(1, len(nums)):
if down and nums[i - 1] < nums[i] or nums[i - 1] == nums[i]:
up = down = 0
up += nums[i - 1] < nums[i]
down += nums[i - 1] > nums[i]
if up and down:
longest = max(longest, up + down + 1)
return longest

Related

Leetcode 3Sum: why is result duplicated?

I am trying the 15. 3Sum code challenge on LeetCode:
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
Notice that the solution set must not contain duplicate triplets.
Example 1:
Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]
Here is my attempt:
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
if len(nums) < 3:
return []
nums.sort()
ret_val = []
for i in range(len(nums) - 2):
if i - 1 >= 0 and nums[i - 1] == nums[i]:
while nums[i - 1] == nums[i] and i < len(nums) - 2:
i += 1
if nums[i - 1] == nums[i]:
break
j = i + 1
k = len(nums) - 1
# This is our target sum
target = -nums[i]
while j < k:
if j - 1 != i and nums[j - 1] == nums[j]:
while nums[j - 1] == nums[j] and j < k:
j += 1
elif k + 1 != len(nums) and nums[k + 1] == nums[k]:
while nums[k + 1] == nums[k] and j < k:
k -= 1
else:
if nums[j] + nums[k] == target:
ret_val.append([nums[i], nums[j], nums[k]])
j += 1
k -= 1
elif nums[j] + nums[k] < target:
j += 1
else:
k -= 1
return ret_val
There is apparently a bug in my code that I couldn't figure out, even after running the Python Debugger. My code gives the wrong result when I use the input of [-11, 1, -12, -12, 10]. It creates an unnecessary duplicate in the answer. I've never seen this happen before, but Python seems to run the for loop one too many times.
The expected output should be [[-11, 1, 10]], but it instead gives [[-11, 1, 10], [-11, 1, 10]]. Another interesting thing I discovered is that by removing either one or both instances of -12, it ends up giving the correct answer of [[-11, 1, 10]].
What is the problem in my code?
The reason is that your outer loop will sometimes increment i (the loop variable). This is not good, because that incremented i value is scheduled to be i's value in a next iteration of that loop, and so you will have two iterations happening with the same value for i.
Note how Python's for i in range iteration does not take into account any increment you apply to i in one of the iterations. The full range of values will be iterated, no matter what you do with i in the mean time. This is different from more traditional for loop constructs that you find in other programming languages:
for (int i = 0; i < len(nums) - 2; i++)
...as in that case the loop's i++ will just act on whatever value i has at that moment, taking into account any change you made to i during an iteration. Again, this is not how it works with an iterator such as Python's range function returns.
The solution is to just exit the current iteration when you would have wanted to increment i, since you know that the next iteration will have that incremented value for i anyhow.
So change this piece of code:
if i - 1 >= 0 and nums[i - 1] == nums[i]:
while nums[i - 1] == nums[i] and i < len(nums) - 2:
i += 1
if nums[i - 1] == nums[i]:
break
to this:
if i - 1 >= 0 and nums[i - 1] == nums[i]:
continue
That will solve the issue.

Counting the numbers in a list that are larger than their neighbors

I want to find a peak in a list.
I want to find if a number is bigger than his neighbors.
if it is the first object in the list I want to check only if he is bigger than the one after him.
if it is the last object in the list I want to check the one before him.
def peaks(lst):
num = 0
leni = len(lst)
print(leni)
for i in range(1,leni - 1):
if lst[i] > lst[i-1] and lst[i] > lst[i+1]:
num = num + 1
for i in range(leni):
print(i)
if i == 0:
if lst[i] > lst[i+1]:
num = num + 1
elif i == leni+1:
if lst[i] > lst[i-1]:
num = num + 1
return num
This code doesn't work when it should check the last object.
When I try [1,2,3] I get 0 instead of 1.
You could do some trickery to count peaks by making the boundary special cases not so special any more:
def peaks(lst):
lst = [float("-inf")] + lst + [float("-inf")]
return sum(a < b > c for a, b, c in zip(lst, lst[1:], lst[2:]))
Hello and welcome to Stackoverflow!
Note that range(leni) is a sequence of numbers from 0 to leni - 1 inclusive. So your condition i == leni+1 is never satisfied. You may replace it to i == leni - 1.
Note also that you don't need a second loop. You may just replace it with
if lst[0] > lst[1]:
num = num + 1
if lst[-1] > lst[-2]:
num= num + 1
Here lst[-1] is the same as lst[leni - 1] and lst[-2] is the same as lst[leni - 2].
Alternative way.
Check the extremes then check the core, three by three:
def count_peaks(ls):
res = [ls[0] > ls[1], ls[-1] > ls[-2]]
for n in range(len(ls)-2):
a, b, c = ls[n:n+3]
res.insert(-1, b > max([a, c]))
return res
Insert before the last element, to reflect the peaks position in the result.
So, in case of [1,2,1,2,4,5]:
count_peaks([1,2,1,2,4,5])
#=>[False, True, False, False, False, True]
Just return sum(res) instead to get the desired result: 2
It seems like you're just starting with python. Your code is difficult to follow. There are many possible solutions for this problem but I'd advise to write simple code. Then worry about performance.
Readability first!
from typing import List
def peaks(lst: List[int]):
found = 0
for i, current_value in enumerate(lst):
is_first_object = i == 0
is_last_object = i == len(lst) - 1
if is_first_object:
previous_val = None
else:
previous_val = lst[i-1]
if is_last_object:
next_val = None
else:
next_val = lst[i+1]
if is_first_object and not is_last_object:
found += 1 if current_value > next_val else 0
elif is_last_object and not is_first_object:
found += 1 if current_value > previous_val else 0
elif not is_last_object and not is_last_object:
found += 1 if previous_val < current_value > next_val else 0
return found
print(peaks([1, 2, 3]))
you can add proxy value of first element and second last element, at front and last and then check the condition from the first element to second last element
def peaks(array):
if len(array)<2:
return 0
array2 = [array[1]] + array + [array[-2]]
count = sum([1 for i in range(len(array2)-2) if array2[i+2]<array2[i+1] >array2[i]])
return count
try this one
def peaks(lst):
num = 0
leni = len(lst)
for i in range(leni):
if i == 0:
if lst[i] > lst[i+1]:
num = num + 1
elif i == leni-1:
if lst[i] > lst[i-1]:
num = num + 1
else:
if lst[i] > lst[i-1] and lst[i] > lst[i+1]:
num = num + 1
return num
Should do the trick
def peaks(lst):
lst_len = len(lst)
hits = 0
for i in range(1,lst_len-1):
num_to_test = lst[i]
before = lst[i-1]
after = lst[i+1]
if num_to_test > before and num_to_test > after:
hits+=1
# This is for the case lst contains only 1 member and you want to count it as 1
if lst_len == 1:
hits+=1
# For checking the edges
if lst_len > 1:
if lst[0] > lst[1]:
hits+=1
if lst[lst_len-1] > lst[lst_len-2]:
hits+=1
return hits
Counting of direct-neighbor peaks can be a performance-relevant task and the list of implementations is getting longer. Good reasons to compare the runtime. In a first run, I decided to go for one test set only. Obviously, the different implementations may reveal their strength at different lengths of lists. For instance, the optimal implementation of border value handling seems to be a candidate that depends on the list length.
Output:
count_peaks_schwobaseggl: elapsed time 1.44 s
count_peaks_sahasrara62: elapsed time 1.50 s
count_peaks_saikat: elapsed time 2.27 s
count_peaks_tom_wojcik: elapsed time 4.11 s
count_peaks_igian: elapsed time 3.65 s
count_peaks_cloud_balancing: elapsed time 1.86 s
Implementation:
import random
import time
from typing import List
def measure_runtime_in_s(func, test_lists):
start = time.time()
results = []
for test_list in test_lists:
max_cnt = func(test_list)
results.append(max_cnt)
return time.time() - start, results
def run_experiment(funcs, nlists=1000, len_range=(20, 10000), num_range=(-100, 100)):
assert len(funcs) > 0
# generate test data
test_lists = [[random.randint(*num_range) for _ in range(random.randint(*len_range))]
for _ in range(nlists)]
# run it for all implementations and check results
_, ref_results = measure_runtime_in_s(funcs[0], test_lists)
for func in funcs:
failed = False
time_in_s, results = measure_runtime_in_s(func, test_lists)
if not all(result == ref_result for result, ref_result in zip(results, ref_results)):
failed = True
print(
f"{func.__name__}: elapsed time {time_in_s:.2f} s"
+ (" (FAILED TESTS!)" if failed else ""))
def count_peaks_schwobaseggl(lst):
lst = [float("-inf")] + lst + [float("-inf")]
return sum(a < b > c for a, b, c in zip(lst, lst[1:], lst[2:]))
def count_peaks_sahasrara62(array):
if len(array) < 2:
return 0
array2 = [array[1]] + array + [array[-2]]
count = sum([1 for i in range(len(array2) - 2) if array2[i + 2] < array2[i + 1] > array2[i]])
return count
def count_peaks_saikat(lst):
num = 0
leni = len(lst)
for i in range(leni):
if i == 0:
if lst[i] > lst[i + 1]:
num = num + 1
elif i == leni - 1:
if lst[i] > lst[i - 1]:
num = num + 1
else:
if lst[i] > lst[i - 1] and lst[i] > lst[i + 1]:
num = num + 1
return num
def count_peaks_igian(ls):
res = [ls[0] > ls[1], ls[-1] > ls[-2]]
for n in range(len(ls)-2):
a, b, c = ls[n:n+3]
res.insert(-1, b > max([a, c]))
return sum(res) # < modified
def count_peaks_tom_wojcik(lst: List[int]):
found = 0
for i, current_value in enumerate(lst):
is_first_object = i == 0
is_last_object = i == len(lst) - 1
if is_first_object:
previous_val = None
else:
previous_val = lst[i-1]
if is_last_object:
next_val = None
else:
next_val = lst[i+1]
if is_first_object and not is_last_object:
found += 1 if current_value > next_val else 0
elif is_last_object and not is_first_object:
found += 1 if current_value > previous_val else 0
elif not is_last_object and not is_last_object:
found += 1 if previous_val < current_value > next_val else 0
return found
def count_peaks_cloud_balancing(lst):
lst_len = len(lst)
hits = 0
for i in range(1, lst_len - 1):
num_to_test = lst[i]
before = lst[i - 1]
after = lst[i + 1]
if num_to_test > before and num_to_test > after:
hits += 1
# This is for the case lst contains only 1 member and you want to count it as 1
if lst_len == 1:
hits += 1
# For checking the edges
if lst_len > 1:
if lst[0] > lst[1]:
hits += 1
if lst[lst_len - 1] > lst[lst_len - 2]:
hits += 1
return hits
if __name__ == "__main__":
run_experiment([
count_peaks_schwobaseggl,
count_peaks_sahasrara62,
count_peaks_saikat,
count_peaks_tom_wojcik,
count_peaks_igian,
count_peaks_cloud_balancing,
])

check for closest greatest element in list a for every element of list b any optimization suggestion to this code for huge lists

Function:
Check for closest greatest element in 'list a' for every element of 'list b'
if element found, remove that element from original list
so the next element can be compared
list a=[2,3,4], list b=[0,0,7] =>2,3,-1
if no element found, return: -1
and finally prints the count of -1
any suggestions to optimize this code for huge lists
import sys
def next(arr, target): #method returns index of nex greater element or -1
start = 0
end = len(arr) - 1
ans = -1
while (start <= end):
mid = (start + end) // 2
if (arr[mid] <= target):
start = mid + 1
else:
ans = mid
end = mid - 1
return ans
def main():
q=list(map(int,sys.stdin.readline().split())) #list1
r=list(map(int,sys.stdin.readline().split())) #list2
q.sort()
r.sort()
var,count=0,0
for tst in r:
var=next(q,tst)
q.pop(var)
if var == -1:
count+=1
print(count)
main()
sort two list first. then compare two values. if a[i] is not grater than b[i] then go next element of a.
a = [2,3,4]
b = [0,0,7]
a.sort()
b.sort()
c = [];
x = 0;
for i in range(len(b)):
while x < len(a):
if a[x] > b[i]:
c.append(a[x])
break;
x = x + 1;
if x >= len(a):
c.append(-1)
else:
x = x + 1;
for i in c:
print(i)

Why is this merge sort implementation not giving the correct answer?

I have 2 merge sort implementation in Python that looks the same but I have no idea why 1 of them isn't working.
def merge(left, right):
result = []
i,j = 0,0
while i < len(left) and j < len(right):
if left[i] < right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
while (i < len(left)):
result.append(left[i])
i += 1
while (j < len(right)):
result.append(right[j])
j += 1
return result
def merge_sort(L):
if len(L) < 2:
return L[:]
else:
middle = len(L)//2
left = merge_sort(L[:middle])
right = merge_sort(L[middle:])
return merge(left, right)
Running merge_sort([1,3,5,2,4,6]) gives [1, 2, 3, 4, 5, 6] which is the correct answer.
However
def merge1(X,Y):
result = []
n = len(X)
m = len(Y)
i = 0
j = 0
while i < n and j < m:
if X[i] < Y[j]:
result.append(X[i])
i += 1
else:
result.append(Y[j])
j += 1
while (i < n):
result.append(X[i])
i += 1
while (j < n):
result.append(Y[j])
j += 1
return result
def merge_sort1(L):
if len(L) <2 :
return L[:]
else:
middle = len(L) // 2
X = merge_sort1(L[:middle])
Y = merge_sort1(L[middle:])
return merge1(X,Y)
running merge_sort1([1,3,5,2,4,6]) gives a strange answer [1, 2, 3, 4] which is wrong.
But I have no idea why the 2nd attempt gives an incorrect answer when it looks the same with the 1st attempt.
What is the problem and why did this happen?
You wrote while (j < n): instead of while (j < m):

Finding median of two sorted arrays. Can some inequality checks be eliminated?

Working on this problem and post code, my question is whether it is safe to change this line of code
j > 0 and i < m and B[j-1] > A[i]
to
i < m and B[j-1] > A[i]
and also it is safe to change this line of code
i > 0 and j < n and A[i-1] > B[j]
to
i > 0 and A[i-1] > B[j]
I think remove the condition check of j is safe since we already making sure size of A is no bigger than size of B.
Problem statement
There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
Implementation
def median(A, B):
m, n = len(A), len(B)
if m > n:
A, B, m, n = B, A, n, m
if n == 0:
raise ValueError
imin, imax, half_len = 0, m, (m + n + 1) / 2
while imin <= imax:
i = (imin + imax) / 2
j = half_len - i
if j > 0 and i < m and B[j-1] > A[i]:
# i is too small, must increase it
imin = i + 1
elif i > 0 and j < n and A[i-1] > B[j]:
# i is too big, must decrease it
imax = i - 1
else:
# i is perfect
if i == 0: max_of_left = B[j-1]
elif j == 0: max_of_left = A[i-1]
else: max_of_left = max(A[i-1], B[j-1])
if (m + n) % 2 == 1:
return max_of_left
if i == m: min_of_right = B[j]
elif j == n: min_of_right = A[i]
else: min_of_right = min(A[i], B[j])
return (max_of_left + min_of_right) / 2.0
Yes I think, you can remove the condition j > 0, because
j = half_len - i
and you already check that i<m and (m + n + 1) / 2 must be bigger than m since n>=m
same for the second condition j < n. You already make sure that i>0, which ensures that j can at most be (2n+1)/2 - 1 which is smaller than n and thus automatically satisfies your condition

Categories

Resources