Skip to content
10 changes: 8 additions & 2 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
* [Test Max Profit Three](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/buy_sell_stock/test_max_profit_three.py)
* [Test Max Profit Two](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/buy_sell_stock/test_max_profit_two.py)
* [Test Max Profit With Fee](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/buy_sell_stock/test_max_profit_with_fee.py)
* Climb Stairs
* [Test Climb Stairs](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/climb_stairs/test_climb_stairs.py)
* Countingbits
* [Test Counting Bits](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/countingbits/test_counting_bits.py)
* Domino Tromino Tiling
* [Test Domino Tromino Tiling](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/domino_tromino_tiling/test_domino_tromino_tiling.py)
* Duffle Bug Value
Expand Down Expand Up @@ -138,12 +142,16 @@
* 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)
* Heap
* Kclosestelements
* [Test Find K Closest Elements](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/heap/kclosestelements/test_find_k_closest_elements.py)
* Longest Happy String
* [Test Longest Happy String](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/heap/longest_happy_string/test_longest_happy_string.py)
* Maximal Score After K Operations
* [Test Maximal Score](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/heap/maximal_score_after_k_operations/test_maximal_score.py)
* Maximum Subsequence Score
* [Test Max Subsequence Score](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/heap/maximum_subsequence_score/test_max_subsequence_score.py)
* Mergeksortedlists
* [Test Merge K Sorted Lists](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/heap/mergeksortedlists/test_merge_k_sorted_lists.py)
* Min Cost Hire K Workers
* [Test Min Cost To Hire Workers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/heap/min_cost_hire_k_workers/test_min_cost_to_hire_workers.py)
* Min Cost To Connect Sticks
Expand Down Expand Up @@ -676,8 +684,6 @@
* [Battleship](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/battleship/battleship.py)
* Beeramid
* [Test Bearamid](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/beeramid/test_bearamid.py)
* Climb Stairs
* [Test Climb Stairs](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/climb_stairs/test_climb_stairs.py)
* Find Missing Elem
* [Test Find Missing Elem](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/find_missing_elem/test_find_missing_elem.py)
* Hashmap
Expand Down
78 changes: 78 additions & 0 deletions algorithms/dynamic_programming/countingbits/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Counting Bits

Given an integer n, return an array ans of length n + 1 such that for each i (0 <= i <= n), ans[i] is the number of 1's
in the binary representation of i.

## Examples

```text
Example 1:
Input: n = 2
Output: [0,1,1]
Explanation:
0 --> 0
1 --> 1
2 --> 10
```

```
Example 2:
Input: n = 5
Output: [0,1,1,2,1,2]
Explanation:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101
```

## Constraints

- 0 <= `n` <= 10^5

## Solution

This solution uses a bottom-up dynamic programming approach to solve the problem.
The key to this problem lies in the fact that any binary number can be broken down into two parts: the least-significant
(rightmost bit), and the rest of the bits. The rest of the bits can be expressed as the binary number divided by 2
(rounded down), or `i >> 1`.

For example:
- 4 in binary = 100
- rightmost bit = 0
- rest of bits = 10, which is also (4 // 2) = 2 in binary.

When the number is odd,
- 5 in binary = 101
- rightmost bit = 1
- rest of bits = 10, which is also (5 // 2) = 2 in binary.

in the binary representation of i is that number plus 1 if the rightmost bit is 1. We can tell if the last significant
bit is 1 by checking if it is odd.

As an example, we know that the number of 1's in 2 is 1. This information can be used to calculate the number of 1's in 4.
The number of 1's in 4 is the number of 1's in 2 plus 0, because 4 is even.

This establishes a recurrence relationship between the number of 1's in the binary representation of i and the number of
1's in the binary representation of i // 2: if count[i] is the number of 1's in the binary representation of i, then
count[i] = count[i // 2] + (i % 2).

With the recurrence relationship established, we can now solve the problem using a bottom-up dynamic programming approach.
We start with the base case dp[0] = 0, and then build up the solution for dp[i] for i from 1 to n.

![Solution 1](./images/solutions/counting_bits_solution_1.png)
![Solution 2](./images/solutions/counting_bits_solution_2.png)
![Solution 3](./images/solutions/counting_bits_solution_3.png)
![Solution 4](./images/solutions/counting_bits_solution_4.png)
![Solution 5](./images/solutions/counting_bits_solution_5.png)
![Solution 6](./images/solutions/counting_bits_solution_6.png)
![Solution 7](./images/solutions/counting_bits_solution_7.png)
![Solution 8](./images/solutions/counting_bits_solution_8.png)
![Solution 9](./images/solutions/counting_bits_solution_9.png)
![Solution 10](./images/solutions/counting_bits_solution_10.png)
![Solution 11](./images/solutions/counting_bits_solution_11.png)
![Solution 12](./images/solutions/counting_bits_solution_12.png)
20 changes: 20 additions & 0 deletions algorithms/dynamic_programming/countingbits/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from typing import List


def count_bits(n: int) -> List[int]:
"""
Counts the number of 1 bits in the given integer provided returning a list where count[i] stores the count of '1'
bits in the binary form of i.
Args:
n(int): integer
Returns:
list: count of 1 bits where each index contains the count of 1 bits
"""
dp = [0] * (n + 1)

for i in range(1, n + 1):
# this can also be solved as which is faster in Python
# dp[i] = dp[i >> 1] + (i & 1)
dp[i] = dp[i // 2] + (i % 2)

return dp
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.
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.dynamic_programming.countingbits import count_bits

COUNTING_BITS_TEST_CASES = [
(6, [0, 1, 1, 2, 1, 2, 2]),
(2, [0, 1, 1]),
(5, [0, 1, 1, 2, 1, 2]),
(0, [0]),
(7, [0, 1, 1, 2, 1, 2, 2, 3]),
(10, [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2]),
]


class CountingBitsTestCase(unittest.TestCase):
@parameterized.expand(COUNTING_BITS_TEST_CASES)
def test_counting_bits(self, n: int, expected: List[int]):
actual = count_bits(n)
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()
29 changes: 29 additions & 0 deletions algorithms/heap/kclosestelements/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Find K Closest Elements

Given a sorted array nums, a target value target, and an integer k, find the k closest elements to target in the array,
where "closest" is the absolute difference between each element and target. Return these elements in an array, sorted in
ascending order.

## Examples

```text
nums = [-1, 0, 1, 4, 6]
target = 1
k = 3
Output
[-1, 0, 1]
Explanation
Explanation: -1 is 2 away from 1, 0 is 1 away from 1, and 1 is 0 away from 1. All other elements are more than 2 away.
Since we need to return the elements in ascending order, the answer is [-1, 0, 1]
```

```text
nums = [5, 6, 7, 8, 9]
target = 10
k = 2
Output:
[8, 9]
```
18 changes: 18 additions & 0 deletions algorithms/heap/kclosestelements/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from typing import List
import heapq


def k_closest(nums: List[int], k: int, target: int):
heap = []

for num in nums:
diff = abs(num - target)

if len(heap) < k:
heapq.heappush(heap, (-diff, num))
elif diff < -heap[0][0]:
heapq.heappushpop(heap, (-diff, num))

distances = [pair[1] for pair in heap]
distances.sort()
return distances
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import unittest
from typing import List
from parameterized import parameterized
from algorithms.heap.kclosestelements import k_closest

K_CLOSEST_ELEMENTS_TEST_CASES = [
([1, 2, 3, 4, 5], 4, 3, [1, 2, 3, 4]),
([1, 3, 5, 8, 10], 2, 5, [3, 5]),
([3, 4, 7, 10, 15], 3, 8, [4, 7, 10]),
([5, 6, 7, 8, 9], 3, 7, [6, 7, 8]),
([1, 1, 1, 10, 10, 10], 1, 9, [10]),
([1, 2, 3, 4, 5], 2, 6, [4, 5]),
]


class KClosestElementsTestCase(unittest.TestCase):
@parameterized.expand(K_CLOSEST_ELEMENTS_TEST_CASES)
def test_k_closest_elements(
self, nums: List[int], k: int, target: int, expected: List[int]
):
actual = k_closest(nums, k, target)
self.assertEqual(expected, actual)


if __name__ == "__main__":
unittest.main()
18 changes: 18 additions & 0 deletions algorithms/heap/mergeksortedlists/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Merge K Sorted Lists

Given k linked lists, each sorted in ascending order, in a list lists, write a function to merge the input lists into
one sorted linked list.

## Examples

```text
lists = [[3,4,6],[2,3,5],[-1,6]]
3 -> 4 -> 6
2 -> 3 -> 5
-1 -> 6

Output
[-1,2,3,3,4,5,6,6]

-1 -> 2 -> 3 -> 3 -> 4 -> 5 -> 6 -> 6
```
53 changes: 53 additions & 0 deletions algorithms/heap/mergeksortedlists/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from typing import List
import heapq


def merge_k_lists(lists: List[List[int]]) -> List[int]:
"""
Merges k sorted lists into one list
Args:
lists(list): list of lists
Returns:
list: Combined list from the input list that is sorted
"""
# Validate that the input provided is not empty. If it is empty, return an empty list. Nothing more to do here
if not lists:
return []

# Check if any of the lists provided is empty. If we have an empty list, we return, as there is nothing more to do
has_non_empty = any(lst for lst in lists)
if not has_non_empty:
return []

# Initialize our heap. This will be used to keep the lowest value from each list at the root of the heap. Whenever
# a value is popped from the root, the next value is added to the list and if it is the lowest value it will be
# at the root of the heap
min_heap = []

# We will iterate through the list storing only the first values from each list, including the list index and the
# element index which we will use to iterate through a given list. The list index will be used to iterate through
# the provided lists
for idx, lst in enumerate(lists):
if lst:
value, list_index, element_index = lst[0], idx, 0
heapq.heappush(min_heap, (value, list_index, element_index))

# Result will store the final output
result: List[int] = []

# While we still have elements in the heap
while min_heap:
# We get the top element from the heap
value, list_index, element_index = heapq.heappop(min_heap)
# Add the value to the result, at this point, we know this is the smallest value in the lists of lists
result.append(value)

# We check if the element index is less than the current list it can be found in. This means that there are
# still other elements within this list
if element_index + 1 < len(lists[list_index]):
# We add the next value to the heap
next_value = lists[list_index][element_index + 1]
heapq.heappush(min_heap, (next_value, list_index, element_index + 1))

# Return the result
return result
24 changes: 24 additions & 0 deletions algorithms/heap/mergeksortedlists/test_merge_k_sorted_lists.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.heap.mergeksortedlists import merge_k_lists

MERGE_K_LISTS_TEST_CASES = [
([[3, 4, 6], [2, 3, 5], [-1, 6]], [-1, 2, 3, 3, 4, 5, 6, 6]),
([[2, 4, 6], [1, 3, 5]], [1, 2, 3, 4, 5, 6]),
([[1, 4, 5], [1, 3, 4], [2, 6]], [1, 1, 2, 3, 4, 4, 5, 6]),
([], []),
([[]], []),
([[1, 2, 3]], [1, 2, 3]),
]


class MergeKListsTestCase(unittest.TestCase):
@parameterized.expand(MERGE_K_LISTS_TEST_CASES)
def test_merge_k_lists(self, lists: List[List[int]], expected: List[int]):
actual = merge_k_lists(lists)
self.assertEqual(expected, actual)


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