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
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)
* 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


29 changes: 23 additions & 6 deletions algorithms/dynamic_programming/buy_sell_stock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,34 @@
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


def max_profit_two_pointers(prices: List[int]) -> int:
"""
Variation of max_profit using 2 pointers
Complexity Analysis:
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
"""
def max_profit_two_pointers(prices: List[int]) -> int:
"""
Variation of max_profit using 2 pointers
Expand All @@ -31,10 +43,15 @@ def max_profit_two_pointers(prices: List[int]) -> int:
if prices is None or len(prices) < 2:
return 0

number_of_prices = len(prices)

left, right = 0, 1
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
175 changes: 175 additions & 0 deletions algorithms/fast_and_slow/find_duplicate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,178 @@ Explanation 3:
```

> Note: You cannot modify the given array nums. You have to solve the problem using only constant extra space.

## Solution

This solution involves two key steps: identifying the cycle and locating the entry point of this identified cycle, which
represents the duplicate number in the array.

The fast and slow pointers technique detects such cycles efficiently, where one pointer takes one step at a time and the
other advances by two steps. Initially pointing at the start of the array, the position of each pointer for the next step
is determined by the current value they are pointing to. If a pointer points to the value 5 at index 0, its next position
will be index 5. As the pointers traverse the array, both will eventually catch up because there’s a cycle. This is
because, in the noncyclic part, the distance between the pointers increases by one index in each iteration. However, once
both pointers enter the cyclic part, the fast pointer starts closing the gap on the slow pointer, decreasing the distance
by one index each iteration until they meet.

Once the duplicate number is confirmed, we reset one of the pointers (usually the slow pointer) to index 0 while the
other stays at the position where the pointers met. Then, both pointers move one step at a time until they meet again.
With this positioning and pace of pointers, pointers are guaranteed to meet at the cycle’s starting point, corresponding
to the duplicate number.

Now, let’s look at the detailed workflow of the solution:

For this problem, the duplicate number will create a cycle in the nums array. The cycle in the nums array helps identify
the duplicate number.

To find the cycle, we’ll move in the nums array using the `f(x)=nums[x]`, where x is the index of the array. This
function constructs the following sequence to move:

x, nums[x], nums[nums[x]], nums[nums[nums[x]]], ...

In the sequence above, every new element is an element in nums present at the index of the previous element.

Let’s say we have an array,[2, 3, 1, 3]. We’ll start with `x=nums[0]`, which is 2, present at the 0th index of the array
and then move to nums[x], which is 1, present at the 2nd index. Since we found 1 at the 2nd index, we’ll move to the 1st,
and so on. This example shows that if we’re given an array of length n+1, with values in the range [1, n], we can use this
traversal technique to visit all the locations in the array.

The following example illustrates this traversal:

![Solution 1](./images/solutions/find_duplicate_solution_1.png)
![Solution 2](./images/solutions/find_duplicate_solution_2.png)
![Solution 3](./images/solutions/find_duplicate_solution_3.png)
![Solution 4](./images/solutions/find_duplicate_solution_4.png)
![Solution 5](./images/solutions/find_duplicate_solution_5.png)
![Solution 6](./images/solutions/find_duplicate_solution_6.png)
![Solution 7](./images/solutions/find_duplicate_solution_7.png)
![Solution 8](./images/solutions/find_duplicate_solution_8.png)
![Solution 9](./images/solutions/find_duplicate_solution_9.png)
![Solution 10](./images/solutions/find_duplicate_solution_10.png)
![Solution 11](./images/solutions/find_duplicate_solution_11.png)

Now, let's dive deep into how our two parts of the solution work.

In the first part, the slow pointer moves once, while the fast pointer moves twice as fast as the slow pointer until
both of the pointers meet each other. Since the fast pointer is moving twice as fast as the slow pointer, it will be the
first one to enter and move around the cycle. At some point after the slow pointer also enters and moves in the cycle,
the fast pointer will meet the slow pointer. This will be the intersection point.

> Note: The intersection point of the two pointers is, in the general case, not the entry point of the cycle.

![Solution 12](./images/solutions/find_duplicate_solution_12.png)
![Solution 13](./images/solutions/find_duplicate_solution_13.png)
![Solution 14](./images/solutions/find_duplicate_solution_14.png)
![Solution 15](./images/solutions/find_duplicate_solution_15.png)
![Solution 16](./images/solutions/find_duplicate_solution_16.png)
![Solution 17](./images/solutions/find_duplicate_solution_17.png)
![Solution 18](./images/solutions/find_duplicate_solution_18.png)
![Solution 19](./images/solutions/find_duplicate_solution_19.png)
![Solution 20](./images/solutions/find_duplicate_solution_20.png)

In part two, we’ll start moving again in the cycle, but this time, we’ll slow down the fast pointer so that it moves
with the same speed as the slow pointer.

Let’s look at the journeys of the two pointers in part two:

- The slow pointer will start from the 0th position.
- The fast pointer will start from the intersection point.
- After a certain number of steps, let’s call it F, the slow pointer meets the fast pointer. This is the ending point
for both pointers.
- This common ending position will be the entry point of the cycle.

Let’s look at the visual presentation of the second part of our solution:

![Solution 21](./images/solutions/find_duplicate_solution_21.png)
![Solution 22](./images/solutions/find_duplicate_solution_22.png)
![Solution 23](./images/solutions/find_duplicate_solution_23.png)

Now, let’s try to understand how it is that our solution is able to always locate the entry point of the cycle.

Let’s return to the example we just discussed, using this graphical representation:

![Solution 24](./images/solutions/find_duplicate_solution_24.png)

- 7 is the intersection point where the slow and fast pointers will meet.
- 8 is the entry point of the cycle, which is our duplicate number.

The fast pointer is traversing two times faster than the slow pointer. This can be represented by the following equation:

> dfast = 2dslow ———(1)

Here, d represents the number of elements traversed.

Let’s look at the following diagram to see the steps taken by the slow and fast pointers from the starting point to the
intersection point:

![Solution 25](./images/solutions/find_duplicate_solution_25.png)

> A list with a cycle

In the diagram above:

- Green represents the entry point of the cycle.
- Blue represents the intersection point.
- Yellow represents the starting point.
- F represents the steps taken from the starting point to the entry point.
- a represents the steps taken to reach the intersection point from the entry point.
- C represents the cycle length, in terms of the number of steps taken to go once around the cycle.

With this setup in mind, let’s see the distance traveled by the slow and fast pointers. The slow pointer travels F steps
from the starting point to the entry point of the cycle and then takes a steps from the entry point to the intersection
point of the cycle, that is, the point where both pointers intersect. So, we can express the distance traveled by the
slow pointer in the form of this equation:

dslow = F+a ——— (2)

In the time it takes the slow pointer to travel F+a steps, the fast pointer, since it’s traveling twice as fast as the
slow pointer, will have also traveled around the cycle at least once. So, we can say the fast pointer, first, travels F
steps from the starting point to the entry point of the cycle, then travels at least a cycle, and at the end travels a
steps from the entry point to the intersection point of the cycle. Now, we can express the distance traveled by the fast
pointer as the following equation:

dfast = F+C+a ——— (3)

Recall eq. (1):

dfast = 2dslow ——— (1)

If we substitute the equivalent expression of dslow given in eq. (2) and the equivalent expression of dfast given in eq.
(3) into eq. (1), we get:

F + C + a = 2(F + a)

Let’s simplify this equation:

F + C + a = 2F + 2a
C = F + a

Therefore, the distance from the starting point to the intersection point, F+a, equals C.

We can also re-arrange this equality as follows:

F=C−a

Let’s consult our diagram again:

![Solution 26](./images/solutions/find_duplicate_solution_26.png)

As we can see, C−a is, in fact, the distance from the intersection point back to the entry point. This illustrates why,
when we move one pointer forward, starting at the intersection point, and another pointer from the starting point, the
point where they meet is the entry point of the cycle.

> Note: The proof above does not consider the case where F is longer than the length of the cycle. In this situation,
> it’s possible that the fast pointer will go around the cycle more than once. To express this more general case, we can
> say that the distance covered by the fast pointer from the entry point to the intersection point is: F+nC+a, where n
> is a positive integer. As a result, our substitution will take this form: F + nC + a = 2(F+a), which simplifies to
> nC=F+a, that is: F = nC − a. This simply means that after going around the cycle n times, the fast pointer will still
> be a steps behind the entry point of the cycle.

### Time Complexity

The time complexity of the algorithm is O(n), where n is the length of nums. This is because, in each part of the
solution, the slow pointer traverses nums just once.

### Space Complexity

The algorithm takes O(1) space complexity, since we only used constant space to store the fast and slow pointers.
34 changes: 26 additions & 8 deletions algorithms/fast_and_slow/find_duplicate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,38 @@ def find_duplicate_floyd_algo(numbers: List[int]) -> int:

if len(numbers) <= 1:
return -1

slow = numbers[0]
fast = numbers[numbers[0]]

while slow != fast:
# Initialize the fast and slow pointers and make them point the first
# element of the array
slow = fast = numbers[0]

# PART #1
# Traverse in array until the intersection point is found
while True:
# Move the slow pointer using the nums[slow] flow
slow = numbers[slow]
# Move the fast pointer two times fast as the slow pointer using the
# nums[nums[fast]] flow
fast = numbers[numbers[fast]]

fast = 0
# Break the loop when slow pointer becomes equal to the fast pointer, i.e.,
# if the intersection is found
if slow == fast:
break

# PART #2
# Make the slow pointer point the starting position of an array again, i.e.,
# start the slow pointer from starting position
slow = numbers[0]
# Traverse in the array until the slow pointer becomes equal to the
# fast pointer
while fast != slow:
# Move the slow pointer using the nums[slow] flow
slow = numbers[slow]
# Move the fast pointer slower than before, i.e., move the fast pointer
# using the nums[fast] flow
fast = numbers[fast]

return slow
# Return the fast pointer as it points the duplicate number of the array
return fast


def find_duplicate(numbers: List[int]) -> int:
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.
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
Loading