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
10 changes: 5 additions & 5 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
* [Test Generate Permutations](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/backtracking/permutations/generate_permutations/test_generate_permutations.py)
* Restore Ip Addresses
* [Test Restore Ip Addresses](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/backtracking/restore_ip_addresses/test_restore_ip_addresses.py)
* Subsets
* Cascading Subsets
* [Test Cascading Subsets](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/backtracking/subsets/cascading_subsets/test_cascading_subsets.py)
* Find All Subsets
* [Test Find All Subsets](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/backtracking/subsets/find_all_subsets/test_find_all_subsets.py)
* Word Search
* [Constants](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/backtracking/word_search/constants.py)
* [Point](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/backtracking/word_search/point.py)
Expand Down Expand Up @@ -307,11 +312,6 @@
* Stack
* Daily Temperatures
* [Test Daily Temperatures](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/stack/daily_temperatures/test_daily_temperatures.py)
* Subsets
* Cascading Subsets
* [Test Cascading Subsets](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/subsets/cascading_subsets/test_cascading_subsets.py)
* Find All Subsets
* [Test Find All Subsets](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/subsets/find_all_subsets/test_find_all_subsets.py)
* Taxi Numbers
* [Taxi Numbers](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/taxi_numbers/taxi_numbers.py)
* Top K Elements
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import unittest

from algorithms.subsets.cascading_subsets import each_cons
from algorithms.backtracking.subsets.cascading_subsets import each_cons


class CascadingSubsetsTestCase(unittest.TestCase):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,20 @@ Given an array of integers, nums, find all possible subsets of nums, including t
- Bit Manipulation
- Backtracking
- Arrays

---
# Subsets II

Given an integer array nums, that can contain duplicate elements, return all possible subsets while ensuring that each
subset is unique. The output must include unique subsets, and you may return them in any order.

## Constraints

- 1 <= `nums.length` <= 10
- -10 <= `nums[i]` <= 10

## Examples

![Example 1](images/examples/find_all_subsets_ii_example_1.png)
![Example 2](images/examples/find_all_subsets_ii_example_2.png)
![Example 3](images/examples/find_all_subsets_ii_example_3.png)
65 changes: 65 additions & 0 deletions algorithms/backtracking/subsets/find_all_subsets/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from typing import List


def find_all_subsets(nums: List[int]) -> List[List[int]]:
n = len(nums)

if n == 0:
return []
Comment on lines +7 to +8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Empty input returns [] instead of [[]].

Mathematically, the power set of an empty set is {∅}, i.e., [[]]. Returning [] (no subsets at all) deviates from the standard definition. The same applies to find_all_subsets_with_duplicates at lines 28-29. If this is intentional, consider adding a comment explaining the design choice.

🤖 Prompt for AI Agents
In `@algorithms/backtracking/subsets/find_all_subsets/__init__.py` around lines 7
- 8, The base case for empty input is incorrect: change the n == 0 return in
find_all_subsets to return [[]] (a list containing the empty subset) instead of
[] so the power set of an empty list is represented correctly; make the
analogous change in find_all_subsets_with_duplicates (where lines 28-29
currently return []) to return [[]] as well, and add a brief comment in both
functions noting this is the mathematical power set base case.


subsets = []

def backtrack(first, curr):
# Add the current subset to the output
subsets.append(curr[:])
# Generate subsets starting from the current index
for i in range(first, n):
curr.append(nums[i])
backtrack(i + 1, curr)
curr.pop()

backtrack(0, [])
return subsets


def find_all_subsets_with_duplicates(nums: List[int]) -> List[List[int]]:
n = len(nums)

if n == 0:
return []

# Sort in place to ensure that duplicate elements are next to each other, which will be used to skip over the duplicate
# elements in the iteration.
# Time complexity here is O(n log(n)) and space complexity is O(n) due to Timsort requiring space to handle sorting
# in place
nums.sort()

subsets: List[int] = []
result: List[List[int]] = []

def backtrack(idx: int) -> None:
"""
Backtrack to create subsets from the given current index of the element being considered
Args:
idx(int): index of the element being considered.
"""
# Base case, if we reach the end of the list, we include the current subset to the result
if idx == n:
result.append(subsets[:])
return

# Add the current subset to the output
# Include the current element to the subset and move to the next element
subsets.append(nums[idx])
backtrack(idx + 1)
# After recursion, remove it to backtrack and explore the excluded path
subsets.pop()

# Skip the current element(exclude path), but skip duplicates too. If there are duplicate elements, skip them all
# when choosing exclude path. This ensures that we only take one unique subset for each duplicate group
while idx + 1 < n and nums[idx] == nums[idx + 1]:
idx += 1
backtrack(idx + 1)

backtrack(0)
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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import unittest
from typing import List
from parameterized import parameterized
from algorithms.backtracking.subsets.find_all_subsets import (
find_all_subsets,
find_all_subsets_with_duplicates,
)

FIND_ALL_SUBSETS_TEST_CASES = [
([], []),
([9], [[], [9]]),
([1], [[], [1]]),
([0], [[], [0]]),
([2, 4], [[], [2], [4], [2, 4]]),
([1, 2], [[], [1], [2], [1, 2]]),
([2, 5, 7], [[], [2], [5], [2, 5], [7], [2, 7], [5, 7], [2, 5, 7]]),
(
[1, 2, 3, 4],
[
[],
[1],
[2],
[1, 2],
[3],
[1, 3],
[2, 3],
[1, 2, 3],
[4],
[1, 4],
[2, 4],
[1, 2, 4],
[3, 4],
[1, 3, 4],
[2, 3, 4],
[1, 2, 3, 4],
],
),
([3, 6, 9], [[], [3], [3, 6], [3, 6, 9], [3, 9], [6], [6, 9], [9]]),
([1, 2, 3], [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]),
]

FIND_ALL_SUBSETS_WITH_DUPLICATES_TEST_CASES = [
([0], [[], [0]]),
([-1, 1], [[], [-1], [-1, 1], [1]]),
([1, 2, 2], [[], [1], [1, 2], [1, 2, 2], [2], [2, 2]]),
([0, 0], [[], [0], [0, 0]]),
([-2, -2, 1], [[], [-2], [-2, -2], [-2, -2, 1], [-2, 1], [1]]),
([1, 1], [[], [1], [1, 1]]),
(
[4, 4, 4, 1, 4],
[
[],
[1],
[1, 4],
[1, 4, 4],
[1, 4, 4, 4],
[1, 4, 4, 4, 4],
[4],
[4, 4],
[4, 4, 4],
[4, 4, 4, 4],
],
),
]
FIND_ALL_SUBSETS_WITH_DUPLICATES_TEST_CASES.extend(FIND_ALL_SUBSETS_TEST_CASES)


class FindAllSubsetsTestCase(unittest.TestCase):
@parameterized.expand(FIND_ALL_SUBSETS_TEST_CASES)
def test_find_all_subsets(self, nums: List[int], expected: List[List[int]]):
actual = find_all_subsets(nums)
expected_sorted = sorted(expected)
actual_sorted = sorted(actual)
self.assertEqual(expected_sorted, actual_sorted)

@parameterized.expand(FIND_ALL_SUBSETS_WITH_DUPLICATES_TEST_CASES)
def test_find_all_subsets_with_duplicates(
self, nums: List[int], expected: List[List[int]]
):
actual = find_all_subsets_with_duplicates(nums)
expected_sorted = sorted(expected)
actual_sorted = sorted(actual)
self.assertEqual(expected_sorted, actual_sorted)


if __name__ == "__main__":
unittest.main()
22 changes: 0 additions & 22 deletions algorithms/subsets/find_all_subsets/__init__.py

This file was deleted.

47 changes: 0 additions & 47 deletions algorithms/subsets/find_all_subsets/test_find_all_subsets.py

This file was deleted.

Loading