Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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
12 changes: 12 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@
* Valid Path
* [Test Valid Path](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/graphs/valid_path/test_valid_path.py)
* Greedy
* Assign Cookies
* [Test Assign Cookies](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/assign_cookies/test_assign_cookies.py)
* Boats
* [Test Boats To Save People](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/boats/test_boats_to_save_people.py)
* Gas Stations
Expand All @@ -143,8 +145,12 @@
* [Test Majority Element](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/majority_element/test_majority_element.py)
* Min Arrows
* [Test Find Min Arrows](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/min_arrows/test_find_min_arrows.py)
* Minimum Refuel Stops
* [Test Min Refueling Stops](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/minimum_refuel_stops/test_min_refueling_stops.py)
* Spread Stones
* [Test Minimum Moves To Spread Stones](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/spread_stones/test_minimum_moves_to_spread_stones.py)
* Two City Scheduling
* [Test Two City Scheduling](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/two_city_scheduling/test_two_city_scheduling.py)
* Heap
* Kclosestelements
* [Test Find K Closest Elements](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/heap/kclosestelements/test_find_k_closest_elements.py)
Expand Down Expand Up @@ -231,6 +237,10 @@
* [Test Longest Substring K Repeating Chars](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/sliding_window/longest_substring_with_k_repeating_chars/test_longest_substring_k_repeating_chars.py)
* Longest Substring Without Repeating Characters
* [Test Longest Substring Without Repeating Characters](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/sliding_window/longest_substring_without_repeating_characters/test_longest_substring_without_repeating_characters.py)
* Max Points From Cards
* [Test Max Points From Cards](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/sliding_window/max_points_from_cards/test_max_points_from_cards.py)
* Max Sum Of Subarray
* [Test Max Sum Sub Array](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/sliding_window/max_sum_of_subarray/test_max_sum_sub_array.py)
Comment thread
BrianLusina marked this conversation as resolved.
* Repeated Dna Sequences
* [Test Repeated Dna Sequences](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/sliding_window/repeated_dna_sequences/test_repeated_dna_sequences.py)
* Sorting
Expand Down Expand Up @@ -482,9 +492,11 @@
* [Binary Tree](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/binary_tree.py)
* [Test Binary Tree](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/test_binary_tree.py)
* [Test Binary Tree Deserialize](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/test_binary_tree_deserialize.py)
* [Test Binary Tree Invert Tree](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/test_binary_tree_invert_tree.py)
* [Test Binary Tree Min Camera Cover](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/test_binary_tree_min_camera_cover.py)
* [Test Binary Tree Serialize](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/test_binary_tree_serialize.py)
* [Test Binary Tree Visible Nodes](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/test_binary_tree_visible_nodes.py)
* [Tree Utils](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/tree/tree_utils.py)
* [Utils](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/binary/utils.py)
* Btree
* [Node](https://github.com/BrianLusina/PythonSnips/blob/master/datastructures/trees/btree/node.py)
Expand Down
2 changes: 0 additions & 2 deletions algorithms/dynamic_programming/buy_sell_stock/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,3 @@ Output: 6
- Arrays
- Dynamic Programming
- Greedy


20 changes: 13 additions & 7 deletions algorithms/dynamic_programming/buy_sell_stock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,23 @@
def max_profit(prices: List[int]) -> int:
"""
Find the maximum profit that can be made from buying and selling a stock once

Time complexity is O(n) as we iterate through each price to get the maximum profit
Space complexity is O(1) as no extra space is used
Args:
prices(list): list of prices
Returns:
int: maximum profit that can be made
"""
if prices is None or len(prices) < 2:
return 0

start_price = prices[0]
current_max_profit = 0

for price in prices:
if price < start_price:
start_price = price
elif price - start_price > current_max_profit:
current_max_profit = price - start_price
for price in prices[1:]:
start_price = min(start_price, price)
current_max_profit = max(current_max_profit, price - start_price)

return current_max_profit

Expand All @@ -28,13 +33,14 @@ def max_profit_two_pointers(prices: List[int]) -> int:
Space: O(1), no extra memory is used
Time: O(n), where n is the size of the input list & we iterate through the list only once
"""
if prices is None or len(prices) < 2:
number_of_prices = len(prices)
if prices is None or number_of_prices < 2:
Comment thread
BrianLusina marked this conversation as resolved.
Outdated
return 0

left, right = 0, 1
current_max_profit = 0

while right < len(prices):
while right < number_of_prices:
low = prices[left]
high = prices[right]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import unittest

from . import max_profit, max_profit_two_pointers
from algorithms.dynamic_programming.buy_sell_stock import (
max_profit,
max_profit_two_pointers,
)


class MaxProfitTestCases(unittest.TestCase):
Expand Down
84 changes: 84 additions & 0 deletions algorithms/greedy/assign_cookies/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Assign Cookies

Assume you are an awesome parent and want to give your children some cookies. But, you should give each child at most
Comment thread
BrianLusina marked this conversation as resolved.
one cookie.

Each child i has a greed factor g[i], which is the minimum size of a cookie that the child will be content with; and
each cookie j has a size s[j]. If s[j] >= g[i], we can assign the cookie j to the child i, and the child i will be content.
Your goal is to maximize the number of your content children and output the maximum number.

## Constraints

- 1 <= greed.length <= 3 * 104
- 0 <= cookies.length <= 3 * 104
- 1 <= greed[i], cookies[j] <= 231 - 1
Comment thread
BrianLusina marked this conversation as resolved.
Outdated

## Examples

Example 1:
```text
Input: g = [1,2,3], s = [1,1]
Output: 1
Explanation: You have 3 children and 2 cookies. The greed factors of 3 children are 1, 2, 3.
And even though you have 2 cookies, since their size is both 1, you could only make the child whose greed factor is 1
content.
You need to output 1.
```

Example 2:
```text
Input: g = [1,2], s = [1,2,3]
Output: 2
Explanation: You have 2 children and 3 cookies. The greed factors of 2 children are 1, 2.
You have 3 cookies and their sizes are big enough to gratify all of the children,
You need to output 2.
```

## Topics

- Array
- Two Pointers
- Greedy
- Sorting

## Solution

Intiutively, we want to give each child the smallest cookie that satisfies them. This allows us to save the larger
cookies for the greedier children and allows us to maximize the number of satisfied children.

The greedy algorithm sorts both the greeds and cookies arrays in ascending order. This places the child with the smallest
greed and the smallest cookie at the front of each array.

For example:

```text
greeds = [1, 3, 3, 4]
cookies = [2, 2, 3, 4]
```

We then initialize two pointers `i` and `j` to the start of the `greeds` and `cookies` arrays, respectively. `i`
represents the current child and `j` represents the current cookie.

If `cookies[j] >= greeds[i]`, that means the current cookie can satisfy the current child. We increment the number of
satisfied children and move to the next child and cookie.

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

If `cookies[j] < greeds[i]`, that means the current cookie cannot satisfy the current child, so we move to the next cookie
to see if it can.

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

We can continue this process until we reach the end of either the greeds or cookies arrays, and return the number of
satisfied children as the result.

### Complexity Analysis

#### Time Complexity

O(n log n + m log m) where n is the number of children and m is the number of cookies. We sort the
greeds and cookies arrays in O(n log n + m log m) time, and then iterate through the arrays in O(n + m) time.

#### Space Complexity

O(1) We only use a constant amount of space for variables.
21 changes: 21 additions & 0 deletions algorithms/greedy/assign_cookies/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from typing import List


def find_content_children(greeds: List[int], cookies: List[int]) -> int:
# This in-place sorting of both g and s results in a time complexity of O(n log(n) + m log(m))
greeds.sort()
cookies.sort()

cookie, greed = 0, 0
count = 0

# We iterate through each greed factor and cookie
while greed < len(greeds) and cookie < len(cookies):
# When we get a cookie that satisfies kid i, we assign that cookie to the child
# and move along, increasing the count as well
if cookies[cookie] >= greeds[greed]:
count += 1
greed += 1
cookie += 1

return count
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.
24 changes: 24 additions & 0 deletions algorithms/greedy/assign_cookies/test_assign_cookies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import unittest
from typing import List
from parameterized import parameterized
from algorithms.greedy.assign_cookies import find_content_children


ASSIGN_COOKIES_TEST_CASES = [
([1, 2, 3], [1, 1], 1),
([1, 2], [1, 2, 3], 2),
([10, 9, 8, 7], [5, 6, 7, 8], 2),
]


class AssignCookiesTestCase(unittest.TestCase):
@parameterized.expand(ASSIGN_COOKIES_TEST_CASES)
def test_find_content_children(
self, greed: List[int], cookies: List[int], expected: int
):
actual = find_content_children(greed, cookies)
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()
51 changes: 51 additions & 0 deletions algorithms/greedy/gas_stations/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,54 @@ Therefore, you can't travel around the circuit once no matter where you start.

- Array
- Greedy

## Solution

If there is more gas along the route than the cost of the route, then there is guaranteed to be a solution to the problem.
So the first step is to check if the sum of the gas is greater than or equal to the sum of the cost. If it is not, then
we return -1.

Next, we iterate through the gas station to find the starting index of our circuit using a greedy approach: whenever we
don't have enough gas to reach the next station, we move our starting gas station to the next station and reset our gas
tank.

We start at the first station, and fill our tank with gas[0] = 5 units of gas. From there, it takes cost[0] = 1 units of
gas to travel to the next station, so we arrive at station 2 (index 1) with 4 units of gas.

![Solution 1](./images/solutions/gas_stations_solution_1.png)
![Solution 2](./images/solutions/gas_stations_solution_2.png)
![Solution 3](./images/solutions/gas_stations_solution_3.png)

At station 2, we fill our tank with gas[1] = 2 units of gas, for a total of 6 units of gas. It takes cost[1] = 5 units
of gas to travel to the next station, so we arrive at station 3 with 1 unit of gas

![Solution 4](./images/solutions/gas_stations_solution_4.png)
![Solution 5](./images/solutions/gas_stations_solution_5.png)

Now at station 3, we fill our tank with gas[2] = 0 units of gas, for a total of 1 unit of gas. It takes cost[2] = 5 units
of gas to travel to the next station, which we don't have.

This is where our greedy approach comes in. We reset our starting station to the next station i + 1 and reset our gas
tank to 0. We can do this because all other start indexes between 0 and 2 will also run into the same problem of not
having enough gas to reach the next station, so we can rule them out.

Comment thread
BrianLusina marked this conversation as resolved.
![Solution 6](./images/solutions/gas_stations_solution_6.png)
![Solution 7](./images/solutions/gas_stations_solution_7.png)

If we follow this approach of resetting the start index and gas tank whenever we don't have enough gas to reach the next
station, then when we finish iterating, the last start index will be the solution to the problem.

![Solution 8](./images/solutions/gas_stations_solution_8.png)
![Solution 9](./images/solutions/gas_stations_solution_9.png)
![Solution 10](./images/solutions/gas_stations_solution_10.png)
![Solution 11](./images/solutions/gas_stations_solution_11.png)

### Complexity Analysis

#### Time Complexity

O(n) where n is the number of gas stations. We only iterate through the gas stations once.

#### Space Complexity

O(1) We only use constant extra space for variables.
10 changes: 7 additions & 3 deletions algorithms/greedy/gas_stations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ def can_complete_circuit(gas: List[int], cost: List[int]) -> int:

gas_tank, start_index = 0, 0

for i in range(len(gas)):
gas_tank += gas[i] - cost[i]
for gas_station in range(len(gas)):
# can reach next station:
# update remaining fuel
gas_tank += gas[gas_station] - cost[gas_station]

if gas_tank < 0:
start_index = i + 1
# can't reach next station:
# try starting from next station
start_index = gas_station + 1
gas_tank = 0
return start_index
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.
15 changes: 8 additions & 7 deletions algorithms/greedy/gas_stations/test_gas_stations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
from parameterized import parameterized
from algorithms.greedy.gas_stations import can_complete_circuit

CAN_COMPLETE_CIRCUIT_TEST_CASES = [
([1, 2, 3, 4, 5], [3, 4, 5, 1, 2], 3),
([2, 3, 4], [3, 4, 3], -1),
([1, 2], [2, 1], 1),
([5, 2, 0, 3, 3], [1, 5, 5, 1, 1], 3),
]


class CanCompleteCircuitTestCase(unittest.TestCase):
@parameterized.expand(
[
([1, 2, 3, 4, 5], [3, 4, 5, 1, 2], 3),
([2, 3, 4], [3, 4, 3], -1),
([1, 2], [2, 1], 1),
]
)
@parameterized.expand(CAN_COMPLETE_CIRCUIT_TEST_CASES)
def test_can_complete_circuit(self, gas: List[int], cost: List[int], expected: int):
actual = can_complete_circuit(gas, cost)
self.assertEqual(expected, actual)
Expand Down
Loading