Lecture 2 : Medium Problems on arrays¶
Find the Majority Element that occurs more than N/2 times¶
Example 1:
Input Format: N = 3, nums[] = {3,2,3}
Result: 3
Example 2:
Input Format: N = 7, nums[] = {2,2,1,1,1,2,2}
Result: 2
Example 3:
Input Format: N = 10, nums[] = {4,4,2,4,3,4,4,3,2,4}
Result: 4
Approach 1 ( Use count map)¶
In [1]:
from collections import Counter
def majority_element(nums):
count_map = Counter(nums)
result , max_count = nums[0],1
for num,count in count_map.items():
if count > max_count:
max_count = count
result = num
return result
print(majority_element([3,2,3]))
print(majority_element([2,2,1,1,1,2,2]))
print(majority_element([4,4,2,4,3,4,4,3,2,4]))
3 2 4
Complexity¶
O(N) : N = len(nums)
O(N) : N = len(nums)
Approach 2 (Moore's Voting Algorithm)¶
- assume first element is the result , with count = 1
- run from 1:n-1
- when we encounter same element , increment count
- when we encounter differet element , decrement count
- whenever count =0 , switch the resule to the elemnt with count =1
In [1]:
def majority_element(nums):
count , result = 1,nums[0]
n = len(nums)
for num in nums[1:]:
if num == result:
count +=1
else:
count -=1
if count ==0:
count =1
result = num
return result
print(majority_element([3,2,3]))
print(majority_element([2,2,1,1,1,2,2]))
print(majority_element([4,4,2,4,3,4,4,3,2,4]))
3 2 4
Complexity¶
O(N) : N = len(nums)
O(1)
Kadane's Algorithm : Maximum Subarray Sum in an Array¶
Input: arr = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Input: arr = [1]
Output: 1
Approach 1 (Brute Force)¶
- We will take 3 loops
- i : 0 -> n-1
- j : 0 -> n-1
- result = max(sum9i,j)
In [ ]:
import sys
def max_subarray(nums):
n = len(nums)
result = -sys.maxsize -1
for i in range(n):
for j in range(i,n):
sum =0
for k in range(i,j+1):
sum += nums[k]
result = max(result,sum)
return result
print(max_subarray([-2,1,-3,4,-1,2,1,-5,4]))
print(max_subarray([1]))
print(max_subarray([5,4,-1,7,8]))
6 1 23
Complexity¶
O(N ^3) : N = len(nums)
O(1)
Approach 2¶
- Get rid of extra loop k
- instead of
for k in range(i,j+1):
sum += nums[k]
- use
sum += nums[j]
In [ ]:
import sys
def max_subarray(nums):
n = len(nums)
result = -sys.maxsize -1
for i in range(n):
sum =0
for j in range(i,n):
sum += nums[j]
result = max(result,sum)
return result
print(max_subarray([-2,1,-3,4,-1,2,1,-5,4]))
print(max_subarray([1]))
6 1
Complexity¶
O(N^2) : N = len(nums)
O(1)
Approach 3 (Kadan's algo)¶
- Move the sum outside all loops
- Any time sum is < 0 , sum =0 and dont include it in the subarray
In [9]:
def max_subarray(nums):
result,sum = nums[0],0
for num in nums:
sum += num
result = max(result,sum)
if sum < 0 :
sum =0
return result
print(max_subarray([-2,1,-3,4,-1,2,1,-5,4]))
print(max_subarray([1]))
print(max_subarray([5,4,-1,7,8]))
6 1 23
Approach 4 (Kadan's clenest code)¶
In [11]:
def max_subarray(nums):
result,sum = nums[0],0
for num in nums:
sum += num
result = max(result,sum)
sum = max(0,sum)
return result
print(max_subarray([-2,1,-3,4,-1,2,1,-5,4]))
print(max_subarray([1]))
print(max_subarray([5,4,-1,7,8]))
6 1 23
Complexity¶
O(N) : N = len(nums)
O(1)
Stock Buy And Sell¶
Input: prices = [7,1,5,3,6,4]
Output: 5
Input: prices = [7,6,4,3,1]
Output: 0
Approach 1 (Brute Force)¶
Buy at each day and calculate profit and find max
In [1]:
def max_profit(prices):
n = len(prices)
result =0
for i in range(n):
profit =0
for j in range(i+1,n):
profit = prices[j] - prices[i]
result = max(profit,result)
return result
print(max_profit([7,1,5,3,6,4]))
print(max_profit([7,6,4,3,1]))
5 0
Complexity¶
O(N^2): N = len(prices)
O(1)
Approach 2¶
- calculate min_buy price
- calculate max_profit
In [13]:
def max_profit(prices):
min_price , result = prices[0],0
for price in prices:
profit = price - min_price
result = max(profit,result)
min_price = min(min_price,price)
return result
print(max_profit([7,1,5,3,6,4]))
print(max_profit([7,6,4,3,1]))
5 0
Complexity¶
O(N) : N= len(prices)
O(1)
Leaders in an array¶
Problem Statement: Given an array, print all the elements which are leaders. A Leader is an element that is greater than all of the elements on its right side in the array.
arr = [4, 7, 1, 0]
7 1 0
arr = [10, 22, 12, 3, 0, 6]
22 12 6
Approach 1 (Brute Force)¶
- check_leader
- run check_leader for each element
In [3]:
def leaders_array(nums):
n = len(nums)
result = [nums[n-1]]
def check_leader(i):
for j in range(i+1,n):
if nums[j] > nums[i]:
return False
return True
for i in range(n-2,-1,-1):
if check_leader(i):
result.append(nums[i])
return result
print(leaders_array([4, 7, 1, 0]))
print(leaders_array([10, 22, 12, 3, 0, 6]))
[0, 1, 7] [6, 12, 22]
Complexity¶
O(N ^2) : N = len(nums)
O(1)
Approach 2¶
- move backwards
- leep track of max till that point
In [4]:
def leaders_array(nums):
n = len(nums)
result = [nums[n-1]]
max_val = nums[n-1]
for i in range(n-2,-1,-1):
if nums[i] > max_val:
result.append(nums[i])
max_val = nums[i]
return result
print(leaders_array([4, 7, 1, 0]))
print(leaders_array([10, 22, 12, 3, 0, 6]))
[0, 1, 7] [6, 12, 22]
Complexity¶
O(N)
O(1)
Longest Consecutive Sequence in an Array¶
Input: [100, 200, 1, 3, 2, 4]
Output: 4
Input: [3, 8, 5, 7, 6]
Output: 4
Approach 1¶
- linear search for each sequence
- max of each count = result
In [14]:
def longest_consecutive_length(nums):
def linear_search(target):
for num in nums:
if num == target:
return True
return False
result =1
for num in nums:
count =0
val = num
while linear_search(val):
count +=1
val +=1
result = max(result,count)
return result
print(longest_consecutive_length([100, 200, 1, 3, 2, 4]))
print(longest_consecutive_length([3, 8, 5, 7, 6]))
4 4
Complexity¶
O(N^2) : N = len(nums)
O(1)
Approach 2¶
- create a set of nums for O(1) lookup
- only start from beg from sequence
- keep track of count
- result = max(count)
In [15]:
def longest_consecutive_length(nums):
nums_set = set(nums)
result =0
for num in nums:
# start of sequence
if num -1 not in nums_set:
count =0
val = num
while val in nums_set:
count +=1
val +=1
result = max(count,result)
return result
print(longest_consecutive_length([100, 200, 1, 3, 2, 4]))
print(longest_consecutive_length([3, 8, 5, 7, 6]))
4 4
Complexity¶
O(N) : N = len(nums) because each element is only accessed twice
O(N) : N = len(nums) becuase of set
Count Subarray sum Equals K¶
Given an array of integers and an integer k, return the total number of subarrays whose sum equals k. must be contiguius nums can be negative
Input Format: N = 4, array[] = {3, 1, 2, 4}, k = 6
Result: 2
Input Format: N = 3, array[] = {1,2,3}, k = 3
Result: 2
Approach 1 Brute Force¶
- find the sum from each index , as soon as it becomes k , increment the count
In [9]:
def count_sub_array(nums,k):
count =0
n = len(nums)
for i in range(n):
sum = nums[i]
if sum ==k:
count +=1
for j in range(i+1,n):
sum += nums[j]
if sum ==k:
count +=1
return count
print(count_sub_array([3,1,2,4],6))
print(count_sub_array([1,2,3],3))
2 2
Make it cleaner and remove duplicate¶
In [10]:
def count_sub_array(nums,k):
count =0
n = len(nums)
for i in range(n):
sum =0
for j in range(i,n):
sum += nums[j]
if sum ==k:
count +=1
return count
print(count_sub_array([3,1,2,4],6))
print(count_sub_array([1,2,3],3))
2 2
Complexity¶
- O(N^2) : N = len(nums)
- O(1)
Approach 2 (Use prefix sum)¶
instead of traversing the whole array every time
we will store the prefix_sum in in map
prefix_sum = sum of all the elements till that index(including)
if prefix_sum[i] - prefix_sum[j] = k , then subarray from index j+1 to i has sum k
In [13]:
def count_sub_array(nums,k):
count =0
prefix_sum =0
prefix_count = {0 : 1}
for num in nums:
prefix_sum += num
if prefix_sum - k in prefix_count:
count += prefix_count[prefix_sum -k]
prefix_count[prefix_sum] = prefix_count.get(prefix_sum,0)+1
return count
print(count_sub_array([3,1,2,4],6))
print(count_sub_array([1,2,3],3))
2 2
Complexity¶
- O(N) : N = len(nums)
- O(N) : N = len(nums)
Two Sum : Check if a pair with given sum exists in Array¶
Input Format: N = 5, arr[] = {2,6,5,8,11}, target = 14
Result: YES (for 1st variant)
Input Format: N = 5, arr[] = {2,6,5,8,11}, target = 15
Result: NO (for 1st variant)
Approach 1 (Brute Force)¶
In [2]:
def two_sum(nums,target):
n = len(nums)
for i in range(n):
for j in range(i+1,n):
if (nums[i] + nums[j]) == target:
return True
return False
print(two_sum([2,6,5,8,11],14))
print(two_sum([2,6,5,8,11],15))
True False
Complexity¶
- N = len(nums)
- O(N^2)
- O(1)
Approach 2 (Via Sorting)¶
In [3]:
def two_sum(nums,target):
n = len(nums)
nums.sort()
left , right = 0 , n-1
while left < right:
current_sum = nums[left] + nums[right]
if current_sum == target:
return True
elif current_sum > target:
right -=1
else:
left +=1
return False
print(two_sum([2,6,5,8,11],14))
print(two_sum([2,6,5,8,11],15))
True False
Complexity¶
- N = len(nums)
- O(N log N)
- O(1)
Approach 3 (Index Hash Map)¶
In [4]:
def two_sum(nums,target):
idx_map = {}
for i,num in enumerate(nums):
if target - num in idx_map:
return True
idx_map[num] = i
return False
print(two_sum([2,6,5,8,11],14))
print(two_sum([2,6,5,8,11],15))
True False
Complexity¶
- N = len(nums)
- O(N)
- O(N)
Sort an array of 0s, 1s and 2s¶
Input: nums = [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]
Input: nums = [2,0,1]
Output: [0,1,2]
Input: nums = [0]
Output: [0]
Approach 1 (Use built in sort)¶
In [13]:
def sort_list(nums):
nums.sort()
return nums
print(sort_list([2,0,2,1,1,0]))
print(sort_list([2,0,1]))
print(sort_list([0]))
[0, 0, 1, 1, 2, 2] [0, 1, 2] [0]
Complexity¶
- N = len(nums)
- O(N log N)
- O(1)
Approach 2 (via freq count)¶
Does not work in place
In [ ]:
def sort_list(nums):
res = []
freq_count = {}
for num in nums:
freq_count[num] = freq_count.get(num,0) +1
res.extend([0]*freq_count.get(0,0))
res.extend([1]*freq_count.get(1,0))
res.extend([2]*freq_count.get(2,0))
return res
print(sort_list([2,0,2,1,1,0]))
print(sort_list([2,0,1]))
print(sort_list([0]))
[0, 0, 1, 1, 2, 2] [0, 1, 2] [0]
Comlpexity¶
- N = len(nums)
- O(N)
- O(N)
Approach 3 (Dutch National Flag Algo)¶
- Three Ptr (low, high ) = 0 , n-1
- mid = 0 --> high
- if nums[mid] is 0 => swap(low,mid) , inc (left, right)
- if nums[mid] is 1 => inc (mid)
- if nums[mid] is 2 => swap(high,mid) dec (high)
In [15]:
def sort_list(nums):
low =0
mid =0
high = len(nums) -1
while mid <= high:
if nums[mid] == 0:
nums[low],nums[mid] = nums[mid],nums[low]
mid +=1
low +=1
elif nums[mid] ==1:
mid +=1
else:
nums[high],nums[mid] = nums[mid],nums[high]
high -=1
return nums
print(sort_list([2,0,2,1,1,0]))
print(sort_list([2,0,1]))
print(sort_list([0]))
[0, 0, 1, 1, 2, 2] [0, 1, 2] [0]
Complexity¶
- N = len(nums)
- O(N)
- O(1)
Kadane's Algorithm : Maximum Subarray Sum in an Array¶
Input: arr = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Input: arr = [1]
Output: 1
Approach 1 (Brute Force)¶
In [17]:
def max_subarray_sum(nums):
res = nums[0]
n = len(nums)
for i in range(n):
win_sum =0
for j in range(i,n):
win_sum += nums[j]
res = max(res,win_sum)
return res
print(max_subarray_sum([-2,1,-3,4,-1,2,1,-5,4]))
print(max_subarray_sum([1]))
6 1
Complexity¶
- N = len(nums)
- O(N^2)
- O(1)
Approach 2 (Kadan's Algo)¶
- Resets curr_sum to 0 whenever it becomes negative (since any negative sum would reduce the next subarray's total)
In [18]:
def max_subarray_sum(nums):
res = nums[0]
n = len(nums)
curr_sum =0
for i in range(n):
curr_sum += nums[i]
res = max(res,curr_sum)
if curr_sum <0:
curr_sum =0
return res
print(max_subarray_sum([-2,1,-3,4,-1,2,1,-5,4]))
print(max_subarray_sum([1]))
6 1
Complexity¶
- N = len(nums)
- O(N)
- O(1)
Print any one elements of the sub array with maximum sum¶
Input: arr = [-2,1,-3,4,-1,2,1,-5,4]
Output: [4,-1,2,1]
Input: arr = [1]
Output: [1]
Approach 1 (Brute Force)¶
In [21]:
def subarray_max_sum(nums):
start = 0
end = 0
n = len(nums)
max_sum = nums[0]
for i in range(n):
curr_sum = 0
for j in range(i,n):
curr_sum += nums[j]
if curr_sum > max_sum :
max_sum = curr_sum
start = i
end = j
return nums[start:end +1]
print(subarray_max_sum([-2,1,-3,4,-1,2,1,-5,4]))
print(subarray_max_sum([1]))
[4, -1, 2, 1] [1]
Complexity¶
- N = len(nums)
- O(N^2)
- O(1)
Approach 2 (Kadan's Algo)¶
In [26]:
def subarray_max_sum(nums):
max_start = 0
max_end = 0
curr_start = 0
n = len(nums)
max_sum = nums[0]
curr_sum = 0
for i in range(n):
curr_sum += nums[i]
if curr_sum > max_sum:
max_sum = curr_sum
max_end = i
max_start = curr_start
if curr_sum < 0:
curr_sum = 0
curr_start = i+1
return nums[max_start:max_end+1]
print(subarray_max_sum([-2,1,-3,4,-1,2,1,-5,4]))
print(subarray_max_sum([1]))
print(subarray_max_sum([5,-10,5]))
print(subarray_max_sum([-5,-2,-3]))
[4, -1, 2, 1] [1] [5] [-2]
Complexity¶
- N = len(nums)
- O(N)
- O(1)