Skip to content
Merged
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
22 changes: 12 additions & 10 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,20 +206,32 @@
* 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
* [Test Pair With Sum In Array](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/pair_with_sum_in_array/test_pair_with_sum_in_array.py)
* Rain Water Trapped
* [Test Trapped Rain Water](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/rain_water_trapped/test_trapped_rain_water.py)
* Reverse Array
* [Test Reverse Array](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/two_pointers/reverse_array/test_reverse_array.py)
* Sort Colors
* [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 +585,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,12 +611,8 @@
* [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
* [Test Trapped Rain Water](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/rain_water_trapped/test_trapped_rain_water.py)
* Rotation
* Cyclic Rotation
* [Test Cyclic Rotation](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/rotation/cyclic_rotation/test_cyclic_rotation.py)
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_11.png)
![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[int]):
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()
107 changes: 107 additions & 0 deletions algorithms/two_pointers/rain_water_trapped/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Trapping Rain-Water

Given an integer array A of non-negative integers representing an elevation map where the width of each bar is 1,
compute how much water it is able to trap after raining.

Input Format
The only argument given is integer array A.

Output Format
Return the total water it is able to trap after raining.

```plain
Example Input
Input 1:

A = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
Input 2:

A = [1, 2]

Example Output
Output 1:

6
Output 2:

0

Example Explanation
Explanation 1:

In this case, 6 units of rain water (blue section) are being trapped.
Explanation 2:

No water is trapped.
```

## Related Topics

- Array
- Two Pointers
- Dynamic Programming
- Stack
- Monotonic Stack

## Solution

We can use the two-pointer technique to solve this problem in O(n) time and O(1) space.
In order for any index in the array to be able to trap rain water, there must be higher bars on both the left and right
side of the index. For example, index 2 in the following array has height 1. It can trap water because there are higher
bars to the left and right of it.

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

To calculate the exact amount of water that can be trapped at index 2, we first take the minimum height of the highest
bars to the left and right of it, which in this case is 4. We then subtract the height of the bar at index 2, which is 1,

![Solution 2](./images/solutions/trapped_rain_water_solution_2.png)

So if we knew the height of the highest bars to the left and right of every index, we could iterate through the array
and calculate the amount of water that can be trapped at each index.
But we don't need to know the exact height of both the highest bars to the left and right of every index. For example,
let's say we know the highest bar to the right of index 7 with height 0 has a height of 2.

![Solution 3](./images/solutions/trapped_rain_water_solution_3.png)

If we also knew that there exists a higher bar than 2 anywhere to the left of index 7, then we also know that the minimum
height of the highest bars to the left and right of index 7 is 2. This means that we have enough information to calculate
the amount of water that can be trapped at index 7, which is 2 - 0 = 2.
This is the insight behind how the two-pointer technique can be used to solve this problem. We initialize two pointers
left and right at opposite ends of the array. We also keep two variables leftMax and rightMax to keep track of the highest
bars each pointer has seen.

![Solution 4](./images/solutions/trapped_rain_water_solution_4.png)

We now use the values of leftMax and rightMax to visit every single index in the array exactly once. We start by
comparing leftMax and rightMax. In this case, rightMax is smaller than leftMax, so we know that:

1. The maximum height of the highest bar to the right of right - 1 is rightMax
2. There exists a higher bar than rightMax somewhere to the left of right

These two facts mean that we have enough information to calculate the amount of water that can be trapped at index
right - 1. So first we move the right pointer back by 1:

![Solution 5](./images/solutions/trapped_rain_water_solution_5.png)

There are two possible cases to consider when calculating the amount of water that can be trapped at the current index
of right:
1. The height of the bar at index right is smaller than rightMax
2. The height of the bar at index right is greater than or equal to rightMax

In our case, the height of the bar at index right is smaller than rightMax, so we know that the amount of water that
can be trapped at index 1 is rightMax - height[right], and we can move to the next iteration, which follows the same logic:

![Solution 6](./images/solutions/trapped_rain_water_solution_6.png)
![Solution 7](./images/solutions/trapped_rain_water_solution_7.png)
![Solution 8](./images/solutions/trapped_rain_water_solution_8.png)
![Solution 9](./images/solutions/trapped_rain_water_solution_9.png)

Now, we run into case 2, where height[right] is greater than or equal to rightMax. This means we can't trap any water at
this index, so instead we update rightMax to the height of the bar at index right to prepare for the next iteration.

![Solution 10](./images/solutions/trapped_rain_water_solution_10.png)
![Solution 11](./images/solutions/trapped_rain_water_solution_11.png)

The same logic applies when leftMax is less than rightMax, and this continues until every index has been visited exactly
once, for a total time complexity of O(n) and a space complexity of O(1).
27 changes: 27 additions & 0 deletions algorithms/two_pointers/rain_water_trapped/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from typing import List


def trapped_rain_water(heights: List[int]) -> int:
if not heights:
return 0
# Initialize the pointers left and right at both ends of the array
left, right = 0, len(heights) - 1
# initialize left_max and right_max that will keep track of the highest bars each pointer has seen
left_max, right_max = heights[left], heights[right]
# Keeps track of the total trapped rain wayter
result = 0

while left < right:
if left_max < right_max:
left += 1
if heights[left] >= left_max:
left_max = heights[left]
else:
result += left_max - heights[left]
else:
right -= 1
if heights[right] >= right_max:
right_max = heights[right]
else:
result += right_max - heights[right]
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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import unittest
from typing import List
from parameterized import parameterized
from algorithms.two_pointers.rain_water_trapped import trapped_rain_water

TRAPPING_RAIN_WATER = [
([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1], 6),
([1, 2], 0),
([4, 2, 0, 3, 2, 5], 9),
([3, 4, 1, 2, 2, 5, 1, 0, 2], 10),
]


class TrappedRainWaterTestCase(unittest.TestCase):
@parameterized.expand(TRAPPING_RAIN_WATER)
def test_trapping_rain_water(self, heights: List[int], expected: int):
actual = trapped_rain_water(heights)
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()
Loading
Loading