Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions merge_sorted_array.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
'''
88 Merge Sorted Array
https://leetcode.com/problems/merge-sorted-array/description/

You are given two integer arrays nums1 and nums2, sorted in non-decreasing order, and two integers m and n, representing the number of elements in nums1 and nums2 respectively.

Merge nums1 and nums2 into a single array sorted in non-decreasing order.

The final sorted array should not be returned by the function, but instead be stored inside the array nums1. To accommodate this, nums1 has a length of m + n, where the first m elements denote the elements that should be merged, and the last n elements are set to 0 and should be ignored. nums2 has a length of n.

nums1 = [ <-- sorted elements --->, <-- auxiliary space of 0s-->]
nums2 = [ <-- sorted elements --->]

Example 1:
Input: nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
Output: [1,2,2,3,5,6]
Explanation: The arrays we are merging are [1,2,3] and [2,5,6]. The result of the merge is [1,2,2,3,5,6] with the underlined elements coming from nums1.

Example 2:
Input: nums1 = [1], m = 1, nums2 = [], n = 0
Output: [1]
Explanation: The arrays we are merging are [1] and []. The result of the merge is [1].

Example 3:
Input: nums1 = [0], m = 0, nums2 = [1], n = 1
Output: [1]
Explanation: The arrays we are merging are [] and [1]. The result of the merge is [1]. Note that because m = 0, there are no elements in nums1. The 0 is only there to ensure the merge result can fit in nums1.

Solution:
1. Brute Force:
Copy nums2 to the aux space of nums1. Then sort nums1.
Time: O(N + (M+N) log (M+N)) = O((M+N) log (M+N)), Space: O(1)

2. Three-pointer (aka Two-pointer) approach:
Let A = nums1, B = nums2
Initialize three pointers:
a = points to the last element in the sorted region of array A
b = points to the last element in the sorted region of array B = last element of array B
w = points to the last element of array A (last element in the auxiliary space of A)

Compare the last element of A (indexed by a) vs last element of B (indexed by b).
If A[a] > B[b], copy A[w] <-- A[a]. Decrement a and w
If B[b] > A[a], copy A[w] <-- B[b]. Decrement b and w
Doing this repeatedly will exhaust the sorted elements of A or B.

If A is exhausted, copy the remaining elements of B, i.e. copy A[w] <-- B[b]. Decrement b and w

If B is exhausted, do nothing because (the remaining elements of A are already in A and sorted)

https://www.youtube.com/watch?v=XJytDszgN14

During initialization, could we set pointers a and b to the first elements of A and B respectively?
Ans: No, because it could potentially lead to two problems:
a) overwriting elements in A
b) unsorted B (after swapping elements with A)
Short discussion: https://youtu.be/XJytDszgN14?t=393 (6:33 - 9:43)

Time: O(M+N), Space: O(1)
'''

def merge_sorted_array(A, B, M, N):
assert len(A) == M + N, f"Len of array A must be = M+N = {M}+{N} = {M+N}"
assert len(B) == N, f"Len of array B must be = N = {N}"
if N == 0:
return M

a, b = M-1, N-1
w = M + N - 1

while a>=0 and b>=0:
if B[b] >= A[a]:
A[w] = B[b]
b -= 1
else:
A[w] = A[a]
a -= 1
w -= 1

# if pointer a reaches the starting of A, there is nothing to be done with a. Copy the remaining elements of B to A
while b>=0:
A[w] = B[b]
b -= 1
w -= 1


def run_tests():
tests = [([1,2,3,0,0,0], [2,5,6], 3, 3, [1,2,2,3,5,6]), ([1], [], 1, 0, [1]), ([0], [1], 0, 1, [1])]
for test in tests:
A, B, M, N, ans = test[0], test[1], test[2], test[3], test[4]
print(f"\nBefore merge: A = {A}")
merge_sorted_array(A, B, M, N)
print(f"After merge: A = {A}")
passed = (ans == A)
assert passed == True, f"Test case failed"
print(f"Pass: {passed}")

print(f"\nDONE")

run_tests()
151 changes: 151 additions & 0 deletions remove_duplicates_from_sorted_array_2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
'''
80 Remove duplicates from sorted array II
https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/description/

Given an integer array nums sorted in non-decreasing order, remove some duplicates in-place such that each unique element appears at most twice. The relative order of the elements should be kept the same.

Since it is impossible to change the length of the array in some languages, you must instead have the result be placed in the first part of the array nums. More formally, if there are k elements after removing the duplicates, then the first k elements of nums should hold the final result. It does not matter what you leave beyond the first k elements.

Return k after placing the final result in the first k slots of nums.

Do not allocate extra space for another array. You must do this by modifying the input array in-place with O(1) extra memory.

Custom Judge:

The judge will test your solution with the following code:

int[] nums = [...]; // Input array
int[] expectedNums = [...]; // The expected answer with correct length

int k = removeDuplicates(nums); // Calls your implementation

assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
assert nums[i] == expectedNums[i];
}

If all assertions pass, then your solution will be accepted.

Example 1:
Input: nums = [1,1,1,2,2,3]
Output: 5, nums = [1,1,2,2,3,_]
Explanation: Your function should return k = 5, with the first five elements of nums being 1, 1, 2, 2 and 3 respectively.
It does not matter what you leave beyond the returned k (hence they are underscores).

Example 2:
Input: nums = [0,0,1,1,1,1,2,3,3]
Output: 7, nums = [0,0,1,1,2,3,3,_,_]
Explanation: Your function should return k = 7, with the first seven elements of nums being 0, 0, 1, 1, 2, 3 and 3 respectively.
It does not matter what you leave beyond the returned k (hence they are underscores).

Constraints:
1 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
nums is sorted in non-decreasing order.

Solution:
1. Brute Force (w/o bothering about constraints):
Traverse the array, maintain a hash map of freq count of each element.
Then populate the same array where each element is repeated not more than twice.
Time: O(N), Space: O(N)

2. Two-pointer:
Maintain a left and right pointer. Use the right pointer for reading the array and keeping frequency count of repeated elements. Use the left pointer for writing the repeated elements into the array up to min(freq count, K) times.
K = max no. duplicates allowed
Time: O(N), Space: O(1)
https://youtu.be/zIOGLbAFBOM?t=1393
'''
import copy

def remove_duplicates_from_sorted1(nums, K):
'''
K = max no. of duplicates allowed
Two pointer approach: For each increment of fast pointer, read the value of the array at fast pointer, maintain a freq count of that value. Write that value into nums[slow], i.e. nums[slow] = nums[fast]. Increment slow pointer by 1 step.
The writing part into slow pointer is allowed only if count <= K.
'''
N = len(nums)
if N == 0:
return -1
elif N == 1:
return 1
slow = 1
count = 1
for fast in range(1, N):
if nums[fast] == nums[fast-1]:
count += 1
else:
count = 1

if count <= K:
nums[slow] = nums[fast]
slow += 1
return slow

def remove_duplicates_from_sorted2(nums, K):
'''
K = max no. of duplicates allowed
Two pointer: Slightly different code structure
where we increment fast pointer until we hit the next
unique element and then write at the index pointed to by
slow pointer.
'''
N = len(nums)
if N == 0:
return -1
elif N == 1:
return 1
slow = 0
count = 1
for fast in range(1, N):
if nums[fast] == nums[fast-1]:
count += 1
else:
n = 1
while n <= min(count, K):
nums[slow] = nums[fast-1]
slow += 1
n += 1
count = 1

# copy the final repeated element of the sequence
n = 1
while slow <= fast and n <= min(count, K):
nums[slow] = nums[fast]
slow += 1
n += 1
return slow # there are 'slow' no. of elements from index=0...slow-1


def run_tests():
tests = [([1,1,1,2,2,3], 2, [1,1,2,2,3]),
([1,1,1,2,2,3], 1, [1,2,3]),
([1,2], 2, [1,2]),
([1,2], 1, [1,2]),
([0,1,1,2,2,3,3,3,3,3], 2, [0,1,1,2,2,3,3]),
([0,1,1,2,2,3,3,3,3,3], 3, [0,1,1,2,2,3,3,3]),
([0,1,1,1,1,1,2,2,3,3,3,3,3], 2, [0,1,1,2,2,3,3]),
([0,1,1,1,1,1,2,3,3,3,3,3], 2, [0,1,1,2,3,3]),
([0,1,3,3,3,3,3], 2, [0,1,3,3]),
([1,1,1,1,1,3], 2, [1,1,3]),
([1,3,3,3,3], 2, [1,3,3]),
([1,1,1,1,1], 2, [1,1]),
([1], 2, [1])
]

for test in tests:
nums, K, ans = test[0], test[1], test[2]
nums1 = nums.copy()
nums2 = nums.copy()
print(f"\nBefore remove duplicates = {nums}")
print(f"Max no. of duplicates allowed = {K}")
index1 = remove_duplicates_from_sorted1(nums1, K)
index2 = remove_duplicates_from_sorted2(nums2, K)
print(f"1. Num elements = {index1}, nums = {nums1[:index1]}")
print(f"2. Num elements = {index2}, nums = {nums2[:index2]}")
passed = (ans == nums1[:index1] == nums2[:index2])
assert passed == True, f"Test case failed"
print(f"Pass: {passed}")

print(f"\nDONE")

run_tests()
Loading