-
Notifications
You must be signed in to change notification settings - Fork 2
feat(algorithms, dynamic-programming): counting bits #153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
51b6979
feat(algorithms, dynamic-programming): counting bits
BrianLusina 8722c3a
updating DIRECTORY.md
91aef34
Update algorithms/heap/mergeksortedlists/test_merge_k_sorted_lists.py
BrianLusina 2fa26f6
Update algorithms/heap/mergeksortedlists/__init__.py
BrianLusina dce0b1c
Update algorithms/heap/kclosestelements/README.md
BrianLusina 434692c
Update algorithms/heap/kclosestelements/README.md
BrianLusina 9fa0e94
feat(algorithms, dynamic-programming): min cost to paint houses
BrianLusina 28a2794
updating DIRECTORY.md
26343df
docs(algorithms, dynamic-programming): update readme
BrianLusina File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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. | ||
|
|
||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
Binary file added
BIN
+36.1 KB
.../dynamic_programming/countingbits/images/solutions/counting_bits_solution_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+40 KB
...dynamic_programming/countingbits/images/solutions/counting_bits_solution_10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+52.2 KB
...dynamic_programming/countingbits/images/solutions/counting_bits_solution_11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+33.1 KB
...dynamic_programming/countingbits/images/solutions/counting_bits_solution_12.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+42.3 KB
.../dynamic_programming/countingbits/images/solutions/counting_bits_solution_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+53.7 KB
.../dynamic_programming/countingbits/images/solutions/counting_bits_solution_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+44.2 KB
.../dynamic_programming/countingbits/images/solutions/counting_bits_solution_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+52.9 KB
.../dynamic_programming/countingbits/images/solutions/counting_bits_solution_5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+40.8 KB
.../dynamic_programming/countingbits/images/solutions/counting_bits_solution_6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+52.9 KB
.../dynamic_programming/countingbits/images/solutions/counting_bits_solution_7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+44.7 KB
.../dynamic_programming/countingbits/images/solutions/counting_bits_solution_8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+56.6 KB
.../dynamic_programming/countingbits/images/solutions/counting_bits_solution_9.png
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
24
algorithms/dynamic_programming/countingbits/test_counting_bits.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| # Paint House | ||
|
|
||
| You are a renowned painter who is given a task to paint n houses in a row. You can paint each house with one of three colors: Red, Blue, or Green. The cost of painting each house with each color is different and given in a 2D array costs: | ||
|
|
||
| - costs[i][0] = cost of painting house `i` Red | ||
| - costs[i][1] = cost of painting house `i` Blue | ||
| - costs[i][2] = cost of painting house `i` Green | ||
|
|
||
| No two neighboring houses can have the same color. Return the minimum cost to paint all houses. | ||
|
|
||
| ## Constraints | ||
|
|
||
| - 1 ≤ n ≤ 100 | ||
| - costs[i].length == 3 | ||
| - 1 ≤ costs[i][j] ≤ 1000 | ||
|
|
||
| ## Examples | ||
|
|
||
| Example 1 | ||
|
|
||
| ```text | ||
| costs = [[8, 4, 15], [10, 7, 3], [6, 9, 12]] | ||
| Output: 13 | ||
|
|
||
| Explanation: | ||
|
|
||
| House 0: Blue (cost = 4) | ||
| House 1: Green (cost = 3) | ||
| House 2: Red (cost = 6) | ||
| Total = 4 + 3 + 6 = 13 | ||
|
|
||
| ``` | ||
|
|
||
| Example 2 | ||
| ```text | ||
| Input: | ||
|
|
||
| costs = [[5, 8, 6], [19, 14, 13], [7, 5, 12], [14, 5, 9]] | ||
| Output: 30 | ||
|
|
||
| Explanation: Red(5) → Green(13) → Red(7) → Blue(5) = 30 | ||
| ``` | ||
|
|
||
| ## Solution | ||
|
|
||
| The solution implements a space-optimized dynamic programming approach using three variables to track the minimum costs. | ||
|
|
||
| Variable Initialization: | ||
|
|
||
| prev_min_cost_red = 0: Minimum cost if the current house is painted red (color 0) | ||
| prev_min_cost_blue = 0: Minimum cost if the current house is painted blue (color 1) | ||
| prev_min_cost_green = 0: Minimum cost if the current house is painted green (color 2) | ||
|
|
||
| These start at 0 because before painting any house, the cost is 0. | ||
|
|
||
| Main Loop: The algorithm iterates through each house's costs using tuple unpacking: | ||
|
|
||
| ```python | ||
| for cost_red, cost_blue, cost_green in costs: | ||
| # | ||
| ``` | ||
|
|
||
| Where cost_red, cost_blue, cost_green represent the costs of painting the current house with red, blue, and green | ||
| respectively. | ||
|
|
||
| State Transition: For each house, we simultaneously update all three variables: | ||
|
|
||
| ```python | ||
| prev_min_cost_red, prev_min_cost_blue, prev_min_cost_green = min(prev_min_cost_blue, prev_min_cost_green) + cost_red, | ||
| min(prev_min_cost_red, prev_min_cost_green) + cost_blue, min(prev_min_cost_red, prev_min_cost_blue) + cost_green | ||
| ``` | ||
|
|
||
| Breaking this down: | ||
|
|
||
| - New `prev_min_cost_red` (cost if current house is red): `min(prev_min_cost_blue, prev_min_cost_green) + cost_red` | ||
| We take the minimum of the previous costs where the house was NOT red (either blue or green) | ||
| Add the cost of painting the current house red | ||
|
|
||
| - New `prev_min_cost_blue` (cost if current house is blue): `min(prev_min_cost_red, prev_min_cost_green) + cost_blue` | ||
| We take the minimum of the previous costs where the house was NOT blue (either red or green) | ||
| Add the cost of painting the current house blue | ||
|
|
||
| - New `prev_min_cost_green` (cost if current house is green): `min(prev_min_cost_red, prev_min_cost_blue) + cost_green` | ||
| We take the minimum of the previous costs where the house was NOT green (either red or blue) | ||
| Add the cost of painting the current house green | ||
|
|
||
| The simultaneous assignment is crucial here - all three values are calculated using the old values before any updates occur. | ||
|
|
||
| Final Result: After processing all houses, `prev_min_cost_red`, `prev_min_cost_blue`, and `prev_min_cost_green` contain | ||
| the minimum costs to paint all houses with the last house being red, blue, or green respectively. The answer is the | ||
| minimum among these three values: | ||
|
|
||
| `return min(prev_min_cost_red, prev_min_cost_blue, prev_min_cost_green)` | ||
|
|
||
| ### Complexity Analysis | ||
|
|
||
| #### Time Complexity: O(n) | ||
|
|
||
| Where n is the number of houses (length of the costs array). The algorithm iterates through the costs array exactly once, | ||
| performing constant-time operations (comparisons and additions) for each house. | ||
|
|
||
| #### Space Complexity: O(1) | ||
|
|
||
| The algorithm uses only three variables (`prev_min_cost_red`, `prev_min_cost_blue`, `prev_min_cost_green`) to track the | ||
| minimum costs regardless of the input size. The space usage remains constant as it doesn't create any additional data | ||
| structures that scale with the input. The variables are reused and updated in each iteration rather than storing all | ||
| intermediate results. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| from typing import List | ||
|
|
||
|
|
||
| def min_cost_to_paint_houses_alternate_colors(costs: List[List[int]]) -> int: | ||
| """ | ||
| Finds the minimum cost to paint houses in a street without repeating colors consecutively | ||
| Args: | ||
| costs(list): Costs of painting houses as a 2D list | ||
| Returns: | ||
| int: minimum cost of painting houses | ||
| """ | ||
| # If there are no costs of painting the houses, then the minimum cost is 0 | ||
| if not costs: | ||
| return 0 | ||
|
|
||
| # initially, the cost of painting any house any color is 0 | ||
| # Initialize minimum costs for painting up to previous house with each color | ||
| # prev_min_cost_red: min cost if previous house painted red | ||
| # prev_min_cost_blue: min cost if previous house painted blue | ||
| # prev_min_cost_green: min cost if previous house painted green | ||
| prev_min_cost_red = 0 | ||
| prev_min_cost_blue = 0 | ||
| prev_min_cost_green = 0 | ||
|
|
||
| # Iterate through each house cost | ||
| for current_cost in costs: | ||
| cost_red, cost_blue, cost_green = current_cost | ||
|
|
||
| # Calculate minimum cost for current house with each color | ||
| # Current house painted red: add red cost to min of (prev blue, prev green) | ||
| # Current house painted blue: add blue cost to min of (prev red, prev green) | ||
| # Current house painted green: add green cost to min of (prev red, prev blue) | ||
| curr_min_cost_red = min(prev_min_cost_blue, prev_min_cost_green) + cost_red | ||
| curr_min_cost_blue = min(prev_min_cost_red, prev_min_cost_green) + cost_blue | ||
| curr_min_cost_green = min(prev_min_cost_red, prev_min_cost_blue) + cost_green | ||
|
|
||
| # Update previous costs for next iteration | ||
| prev_min_cost_red = curr_min_cost_red | ||
| prev_min_cost_blue = curr_min_cost_blue | ||
| prev_min_cost_green = curr_min_cost_green | ||
|
|
||
| # Return minimum cost among all three color options for the last house | ||
| return min(prev_min_cost_red, prev_min_cost_blue, prev_min_cost_green) |
20 changes: 20 additions & 0 deletions
20
algorithms/dynamic_programming/painthouse/test_min_cost_to_paint_houses.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import unittest | ||
| from typing import List | ||
| from parameterized import parameterized | ||
| from algorithms.dynamic_programming.painthouse import min_cost_to_paint_houses_alternate_colors | ||
|
|
||
| MIN_COST_PAINT_HOUSE = [ | ||
| ([[8, 4, 15], [10, 7, 3], [6, 9, 12]], 13), | ||
| ([[5, 8, 6], [19, 14, 13], [7, 5, 12], [14, 5, 9]], 30), | ||
| ] | ||
|
|
||
|
|
||
| class MinCostToPaintHouseTestCase(unittest.TestCase): | ||
| @parameterized.expand(MIN_COST_PAINT_HOUSE) | ||
| def test_min_cost_to_paint_houses(self, cost: List[List[int]], expected: int): | ||
| actual = min_cost_to_paint_houses_alternate_colors(cost) | ||
| self.assertEqual(expected, actual) | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| unittest.main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | ||
| -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] | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.