Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
18 changes: 10 additions & 8 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@
* [Test Sorted Squared Array](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/arrays/sorted_squared_array/test_sorted_squared_array.py)
* Subsequence
* [Test Is Valid Subsequence](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/arrays/subsequence/test_is_valid_subsequence.py)
* Two Sum
* [Test Two Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/arrays/two_sum/test_two_sum.py)
* Two Sum Less K
* [Test Two Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/arrays/two_sum_less_k/test_two_sum.py)
* Backtracking
* Combination
* [Test Combination 2](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/backtracking/combination/test_combination_2.py)
Expand Down Expand Up @@ -210,10 +206,14 @@
* Taxi Numbers
* [Taxi Numbers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/taxi_numbers/taxi_numbers.py)
* Two Pointers
* Array 3 Pointers
* [Test Array 3 Pointers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/array_3_pointers/test_array_3_pointers.py)
* Find Sum Of Three
* [Test Find Sum Of Three](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/find_sum_of_three/test_find_sum_of_three.py)
* Merge Sorted Arrays
* [Test Merge Sorted Arrays](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/merge_sorted_arrays/test_merge_sorted_arrays.py)
* Move Zeroes
* [Test Move Zeroes](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/move_zeroes/test_move_zeroes.py)
* Next Permutation
* [Test Next Permutation](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/next_permutation/test_next_permutation.py)
* Pair With Sum In Array
Expand All @@ -224,6 +224,12 @@
* [Test Sort Colors](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/sort_colors/test_sort_colors.py)
* Three Sum
* [Test Three Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/three_sum/test_three_sum.py)
* Triangle Numbers
* [Test Triangle Numbers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/triangle_numbers/test_triangle_numbers.py)
* Two Sum
* [Test Two Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/two_sum/test_two_sum.py)
* Two Sum Less K
* [Test Two Sum](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/two_sum_less_k/test_two_sum.py)
* Unique Bsts
* [Unique Bsts](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/unique_bsts/unique_bsts.py)
* Word Count
Expand Down Expand Up @@ -577,8 +583,6 @@
* Allergies
* [Test Allergies](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/allergies/test_allergies.py)
* Arrays
* Array 3 Pointers
* [Test Array 3 Pointers](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/array_3_pointers/test_array_3_pointers.py)
* Can Place Flowers
* [Test Can Place Flowers](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/can_place_flowers/test_can_place_flowers.py)
* Candy
Expand All @@ -605,8 +609,6 @@
* [Test Max Average Subarray](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/maximum_average_subarray/test_max_average_subarray.py)
* Maxlen Contiguous Binary Subarray
* [Test Maxlen Contiguous Binary Subarray](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/maxlen_contiguous_binary_subarray/test_maxlen_contiguous_binary_subarray.py)
* Move Zeroes
* [Test Move Zeroes](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/move_zeroes/test_move_zeroes.py)
* Product Of Array Except Self
* [Test Product Except Self](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/product_of_array_except_self/test_product_except_self.py)
* Rain Water Trapped
Expand Down
18 changes: 9 additions & 9 deletions algorithms/intervals/insert_interval/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,27 @@ Return the updated list of intervals.

## Solution

We first want to create a new list merged to store the merged intervals we will return at the end.
We first want to create a new list `merged` to store the merged intervals we will return at the end.

This solution operates in 3 phases:
1. Add all the intervals ending before newInterval starts to merged.
2. Merge all overlapping intervals with newInterval and add that merged interval to merged.
3. Add all the intervals starting after newInterval to merged.
1. Add all the intervals ending before `newInterval` starts to `merged`.
2. Merge all overlapping intervals with `newInterval` and add that merged interval to `merged`.
3. Add all the intervals starting after `newInterval` to `merged`.

### Phase 1

In this phase, we add all the intervals that end before newInterval starts to merged. This involves iterating through the
intervals list until the current interval no longer ends before newInterval starts (i.e. intervals[i][1] >= newInterval[0]).
In this phase, we add all the intervals that end before `newInterval` starts to `merged`. This involves iterating through the
`intervals` list until the current interval no longer ends before `newInterval` starts (i.e. `intervals[i][1] >= newInterval[0]`).

![Solution 1](./images/solutions/insert_interval_solution_1.png)
![Solution 2](./images/solutions/insert_interval_solution_2.png)

### Phase 2

In this phase, we merge all the intervals that overlap with newInterval together into a single interval by updating
newInterval to be the minimum start and maximum end of all the overlapping intervals. This involves iterating through
the intervals list until the current interval starts after newInterval ends (i.e. intervals[i][0] > newInterval[1]).
When that condition is met, we add newInterval to merged and move onto phase 3.
`newInterval` to be the minimum start and maximum end of all the overlapping intervals. This involves iterating through
the intervals list until the current interval starts after `newInterval` ends (i.e. `intervals[i][0] > newInterval[1]`).
When that condition is met, we add `newInterval` to merged and move onto phase 3.

![Solution 3](./images/solutions/insert_interval_solution_3.png)
![Solution 4](./images/solutions/insert_interval_solution_4.png)
Expand Down
44 changes: 44 additions & 0 deletions algorithms/two_pointers/move_zeroes/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Move Zeroes

Given an integer array nums, move all 0's to the end of it while maintaining the relative order of the non-zero
elements.

Note that you must do this in-place without making a copy of the array.

```plain

Example 1:

Input: nums = [0,1,0,3,12]
Output: [1,3,12,0,0]
Example 2:

Input: nums = [0]
Output: [0]
```

## Related Topics

- Array
- Two Pointers

## Solution

We can solve this problem by keeping a pointer i that iterates through the array and another pointer nextNonZero that
points to the position where the next non-zero element should be placed. We can then swap the elements at i and nextNonZero
if the element at i is non-zero. This way, we can maintain the relative order of the non-zero elements while moving all
the zeroes to the end of the array.

![Solution 1](./images/solutions/move_zeroes_solution_1.png)
![Solution 2](./images/solutions/move_zeroes_solution_2.png)
![Solution 3](./images/solutions/move_zeroes_solution_3.png)
![Solution 4](./images/solutions/move_zeroes_solution_4.png)
![Solution 5](./images/solutions/move_zeroes_solution_5.png)
![Solution 6](./images/solutions/move_zeroes_solution_6.png)
![Solution 7](./images/solutions/move_zeroes_solution_7.png)
![Solution 8](./images/solutions/move_zeroes_solution_8.png)
![Solution 9](./images/solutions/move_zeroes_solution_9.png)
![Solution 10](./images/solutions/move_zeroes_solution_10.png)
![Solution 11](./images/solutions/move_zeroes_solution_1.png)
Comment thread
BrianLusina marked this conversation as resolved.
Outdated
![Solution 12](./images/solutions/move_zeroes_solution_12.png)
![Solution 13](./images/solutions/move_zeroes_solution_13.png)
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ def move_zeroes(nums: List[int]) -> None:
if len(nums) == 1:
return

left_pointer = 0
for current in range(len(nums)):
if nums[current] != 0:
nums[left_pointer], nums[current] = nums[current], nums[left_pointer]
left_pointer += 1
next_non_zero = 0
for idx in range(len(nums)):
if nums[idx] != 0:
if idx != next_non_zero:
nums[next_non_zero], nums[idx] = nums[idx], nums[next_non_zero]
next_non_zero += 1


def move_zeroes_one(nums: List[int]) -> None:
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions algorithms/two_pointers/move_zeroes/test_move_zeroes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import unittest
from typing import List
from parameterized import parameterized
from algorithms.two_pointers.move_zeroes import move_zeroes, move_zeroes_one

MOVE_ZEROES_TEST_CASES = [
([0, 1, 0, 3, 12], [1, 3, 12, 0, 0]),
([0], [0]),
([0, 0, 0], [0, 0, 0]),
([1, 0], [1, 0]),
([2, 0, 4, 0, 9], [2, 4, 9, 0, 0]),
([1, 0, 4, 0, 3, 0, 1], [1, 4, 3, 1, 0, 0, 0]),
([0, 0, 1], [1, 0, 0]),
([1, 2, 3], [1, 2, 3]),
]


class MoveZeroesTestCase(unittest.TestCase):
@parameterized.expand(MOVE_ZEROES_TEST_CASES)
def test_move_zeroes(self, nums: List[int], expected: [List]):
Comment thread
BrianLusina marked this conversation as resolved.
Outdated
move_zeroes(nums)
self.assertEqual(expected, nums)

@parameterized.expand(MOVE_ZEROES_TEST_CASES)
def test_move_zeroes_with_intermediate(self, nums: List[int], expected: List[int]):
move_zeroes_one(nums)
self.assertEqual(expected, nums)


if __name__ == "__main__":
unittest.main()
58 changes: 58 additions & 0 deletions algorithms/two_pointers/three_sum/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,61 @@ Input: nums = [0,0,0]
Output: [[0,0,0]]
Explanation: The only possible triplet sums up to 0.
```

## Solution

We can leverage the two-pointer technique to solve this problem by first sorting the array. We can then iterate through
each element in the array. The problem then reduces to finding two numbers in the rest of the array that sum to the
negative of the current element, which follows the same logic as the Two Sum (Sorted Array) problem.

![Solution 1](./images/solutions/three_sum_solution_1.png)

Since our first triplet sums to 0, we can add it to our result set.

![Solution 2](./images/solutions/three_sum_solution_2.png)
![Solution 3](./images/solutions/three_sum_solution_3.png)
![Solution 4](./images/solutions/three_sum_solution_4.png)
![Solution 5](./images/solutions/three_sum_solution_5.png)
![Solution 6](./images/solutions/three_sum_solution_6.png)

### Avoiding Duplicates

As soon as we find a triplet that sums to 0, we can add it to our result set. We then have to move our left and right
pointers to look for the next triplet while avoiding duplicate triplets. We can do this by moving the left and right
pointers until they point to different numbers than the ones they were pointing to before.
Here we move the left pointer once until it reaches the last -1 in the array. Then, we can move both the left and right
pointers so that they both point to new numbers.

![Solution 7](./images/solutions/three_sum_solution_7.png)
![Solution 8](./images/solutions/three_sum_solution_8.png)

Here we can do another iteration of the Two Sum problem using the new positions of the left and right pointers.

![Solution 9](./images/solutions/three_sum_solution_9.png)
![Solution 10](./images/solutions/three_sum_solution_10.png)
![Solution 11](./images/solutions/three_sum_solution_11.png)

At this point our left and right pointers have crossed, so we can move our iterator to the next number in the array.

### Avoiding Duplicates II

In this case, since the next number in the array is the same as the previous number, we can skip it. We can do this by
moving our iterator until it points to a new number.

![Solution 12](./images/solutions/three_sum_solution_12.png)
![Solution 13](./images/solutions/three_sum_solution_13.png)
![Solution 14](./images/solutions/three_sum_solution_14.png)

And we're ready to start the Two Sum algorithm again, so we reset our left and right pointers, and start the algorithm.

![Solution 15](./images/solutions/three_sum_solution_15.png)
![Solution 16](./images/solutions/three_sum_solution_16.png)
![Solution 17](./images/solutions/three_sum_solution_17.png)

### Termination

Our algorithm terminates when i reaches the 3rd to last element in the array (i.e., i < n - 2). This is because we need
at least 2 more elements after i for left and right to form a triplet.

![Solution 18](./images/solutions/three_sum_solution_18.png)
![Solution 19](./images/solutions/three_sum_solution_19.png)
30 changes: 22 additions & 8 deletions algorithms/two_pointers/three_sum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,48 @@
def three_sum(nums: List[int]) -> List[List[int]]:
"""
Complexity Analysis:
We assume that n is the length of the input array

Time Complexity: O(nlog(n)) + O(n^2) = O(n^2) the O(nlog(n)) is due to sorting
Space Complexity: O(1) as no extra space is taken up
Time Complexity: O(nlog(n)) + O(n^2) = O(n^2) the O(nlog(n)) is due to sorting, overall, the time complexity is O(n²).
This is due to the nested loops in the algorithm. We perform n iterations of the outer loop, and each iteration
takes O(n) time to use the two-pointer technique.

Space Complexity: O(n²) as no extra space is taken up. We need to store all distinct triplets that sum to 0, which
can be at most O(n²) triplets.

Args:
nums (list): input list of integers
Return:
list: list of lists of integers
"""
result = []
# Time Complexity: O(nlog(n))
# Time Complexity: O(nlog(n)) sorting in place. This may incur space complexity of O(n) due to Python's timesort
# using temporary storage to handle the in place sorting
nums.sort()

for idx, num in enumerate(nums):
# Increment to avoid duplicates
if idx > 0 and num == nums[idx - 1]:
continue

left, right = idx + 1, len(nums) - 1

while left < right:
sum_ = num + nums[left] + nums[right]
if sum_ > 0:
total = num + nums[left] + nums[right]
if total > 0:
right -= 1
elif sum_ < 0:
elif total < 0:
left += 1
else:
# add the triplet
result.append([num, nums[left], nums[right]])
left += 1
while nums[left] == nums[left - 1] and left < right:

# move the left pointer to avoid duplicates while it is still less than the right
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1

return result
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 15 additions & 19 deletions algorithms/two_pointers/three_sum/test_three_sum.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
import unittest

from typing import List
from parameterized import parameterized
from algorithms.two_pointers.three_sum import three_sum

THREE_SUM_TEST_CASES = [
([-1, 0, 1, 2, -1, -4], [[-1, -1, 2], [-1, 0, 1]]),
([0, 1, 1], []),
([0, 0, 0], [[0, 0, 0]]),
([-1, 0, 1, 2, -1, -1], [[-1, -1, 2], [-1, 0, 1]]),
([-1, 0, 1, 2, -1, -4], [[-1, -1, 2], [-1, 0, 1]]),
([-1, 0, 1, 2, -1, -4, 2], [[-1, -1, 2], [-1, 0, 1], [-4, 2, 2]]),
([-1, -1, 0, 1, 1, 1, 2], [[-1, -1, 2], [-1, 0, 1]]),
([-1, 0, 1, 2, -1, -4, -1, 2, 1], [[-1, -1, 2], [-1, 0, 1], [-4, 2, 2]]),
]

class ThreeSumTestCases(unittest.TestCase):
def test_one(self):
"""Should return [[-1, -1, 2], [-1, 0, 1]] for nums = [-1, 0, 1, 2, -1, -4]"""
nums = [-1, 0, 1, 2, -1, -4]
expected = [[-1, -1, 2], [-1, 0, 1]]
actual = three_sum(nums)
self.assertEqual(expected, actual)

def test_two(self):
"""Should return [] for nums = [0, 1, 1]"""
nums = [0, 1, 1]
expected = []
actual = three_sum(nums)
self.assertEqual(expected, actual)

def test_three(self):
"""Should return [[0,0,0]] for nums = [0,0,0]"""
nums = [0, 0, 0]
expected = [[0, 0, 0]]
class ThreeSumTestCases(unittest.TestCase):
@parameterized.expand(THREE_SUM_TEST_CASES)
def test_three_sum(self, nums: List[int], expected: List[List[int]]):
actual = three_sum(nums)
self.assertEqual(expected, actual)

Expand Down
Loading
Loading