Skip to content
Related Articles

Related Articles

Find all Unique Subsets of a given Set

View Discussion
Improve Article
Save Article
  • Difficulty Level : Medium
  • Last Updated : 29 Jul, 2022
View Discussion
Improve Article
Save Article

Given an array A[] of positive integers, print all the unique non-empty subsets of the array 

Note: The set can not contain duplicate elements, so any repeated subset should be considered only once in the output.

Examples: 

Input: A[] = {1, 5, 6}
Output: {{1}, {1, 5}, {1, 6}, {5}, {5, 6}, {6}, {1, 5, 6}}
Explanantion: The number of all the non-empty possible subsets will be 2N-1
Here it will be {1}, {1, 5}, {1, 6}, {5}, {5, 6}, {6} and {1, 5, 6}

Input: A[] = {1, 2, 2}
Output: {{1}, {1, 2}, {1, 2, 2}, {2}, {2, 2}}

 

Intuition: The main idea to solve the problem is as follows:

This problem falls under the classic algorithm of “pick or don’t pick“. 

In this problem, there are two choices of whether we want to include an element in the set or exclude it. For every choice, the element can be appended into the resultant array and then, using backtracking, we will exclude it. This way, the desired set will be generated.

The image shown below shows the choices for each element and how the resultant array is generated at the end of the recursion tree.

Recursion tree to generate all subsets

Approach 1 (Recursion):

Follow the given steps to solve the problem using the above approach:

  • Iterate over the elements one by one.
  • For each element, just pick the element and move ahead recursively and add the subset to the result.
  • Then using backtracking, remove the element and continue finding the subsets and adding them to the result.

Below is the implementation for the above approach:

C++




// C++ code to implement the approach
 
#include <bits/stdc++.h>
using namespace std;
 
// Helper function to find all unique subsets
void findSubsets(vector<int>& v, int idx,
                 vector<int>& subset,
                 set<vector<int> >& result)
{
    // If the current subset is not empty
    // insert it into the result
    if (!subset.empty())
        result.insert(subset);
 
    // Iterate over every element in the array
    for (int j = idx; j < v.size(); j++) {
 
        // Pick the element and move ahead
        subset.push_back(v[j]);
        findSubsets(v, j + 1, subset, result);
 
        // Backtrack to drop the element
        subset.pop_back();
    }
}
 
// Function to return all unique subsets
vector<vector<int> > solve(vector<int>& v)
{
    // To store the resulting subsets
    set<vector<int> > result;
    vector<int> subset;
 
    // Helper function call
    findSubsets(v, 0, subset, result);
 
    vector<vector<int> > res;
    for (auto v : result)
        res.push_back(v);
 
    return res;
}
 
// Driver code
int main()
{
    vector<int> A = { 1, 2, 2 };
 
    // Function call
    vector<vector<int> > result = solve(A);
 
    // Print all unique subsets
    for (auto v : result) {
        for (int i : v) {
            cout << i << " ";
        }
        cout << "\n";
    }
    return 0;
}


Java




// Java code to implement the above approach
 
import java.io.*;
import java.util.*;
 
class GFG {
 
    // Helper function to find all unique subsets
    static void findSubsets(List<Integer> v, int idx,
                            List<Integer> subset,
                            List<List<Integer> > result)
    {
        // If the current subset is not empty insert it into
        // the result
        if (!subset.isEmpty()) {
            if (!result.contains(subset)) {
                result.add(new ArrayList<>(subset));
            }
        }
 
        // Iterate over every element in the array
        for (int j = idx; j < v.size(); j++) {
            // Pick the element and move ahead
            subset.add(v.get(j));
            findSubsets(v, j + 1, subset, result);
 
            // Backtrack to drop the element
            subset.remove(subset.size() - 1);
        }
    }
 
    // Function to return all unique subsets
    static List<List<Integer> > solve(List<Integer> v)
    {
        // To store the resulting subsets.
        List<List<Integer> > result
            = new ArrayList<List<Integer> >();
        List<Integer> subset = new ArrayList<Integer>();
 
        // Helper function call
        findSubsets(v, 0, subset, result);
 
        List<List<Integer> > res
            = new ArrayList<List<Integer> >();
        for (int i = 0; i < result.size(); i++) {
            res.add(new ArrayList<>(result.get(i)));
        }
 
        return res;
    }
 
    public static void main(String[] args)
    {
        List<Integer> A = new ArrayList<Integer>();
        A.add(1);
        A.add(2);
        A.add(2);
 
        List<List<Integer> > result
            = new ArrayList<List<Integer> >();
 
        // Function call
        result = solve(A);
 
        // print all unique subsets
        for (int i = 0; i < result.size(); i++) {
            List<Integer> temp = result.get(i);
            for (int j = 0; j < temp.size(); j++) {
                System.out.print(temp.get(j) + " ");
            }
            System.out.println();
        }
    }
}
 
// This code is contributed by lokesh (lokeshmvs21).


Output

1 
1 2 
1 2 2 
2 
2 2 

Time complexity: O(N * log(2N) * 2N) = O(N2 * 2N)
Auxiliary space: O(2N)

Approach 2 (Iterative Approach):

Follow the given steps to solve the problem using the above approach:

  • Iterate through every number, and there are two choices:
    • Either include the element, then add it into all subsets.
    • Or exclude the element, then don’t do anything.

Below is the implementation for the above approach:

C++




// C++ code to implement the approach
 
#include <bits/stdc++.h>
using namespace std;
 
// Function to return all unique subsets
vector<vector<int> > solve(vector<int>& v)
{
    // To store the resulting subsets
    set<vector<int> > res;
    vector<int> subset;
    vector<vector<int> > result;
 
    // Insert it into the resultant vector
    res.insert(subset);
 
    // Iterate over all elements
    for (int i = 0; i < v.size(); i++) {
        int N = res.size();
        for (auto it = res.begin();
             it != res.end() and N > 0; it++) {
 
            // Iterate through every subset
            // generated till now and inset
            // the current element in the
            // end of it
            subset = *it;
            subset.push_back(v[i]);
            result.push_back(subset);
            N--;
        }
        res.insert(result.begin(), result.end());
        result.clear();
    }
 
    for (auto u : res)
        result.push_back(u);
 
    return result;
}
 
// Driver code
int main()
{
    vector<int> A = { 1, 2, 2 };
 
    // Function call
    vector<vector<int> > result = solve(A);
    for (auto v : result) {
        for (int i : v) {
            cout << i << " ";
        }
        cout << "\n";
    }
    return 0;
}


Output

1 
1 2 
1 2 2 
2 
2 2 

Time complexity: O(N2 * 2N)
Auxiliary space: O(2N)

Approach 3 (Bit Masking):
Prerequisite: Power Set

To solve the problem using the above approach, follow the idea below:

Represent all the numbers from 1 to 2N – 1 where N is the size of the subset in the binary format and the position for which the bits are set to be added to the array and then add that array into the result i.e to use a bit-mask pattern to generate all the combinations. For example,  

Input: A = {1, 5, 6}, N = 3
Explanation: Hence we will check every number from 1 to 7 i.e.till 2n-1 = 23 – 1 = 7
1 => 001 => {6}
2 => 010 => {5}
3 => 011 => {5, 6}
4 => 100 => {1}
5 => 101 => {1, 6}
6 => 110 => {1, 5}
7 => 111 => {1, 5, 6}

Below is the implementation for the above approach:

C++




// C++ code to implement the approach
 
#include <bits/stdc++.h>
using namespace std;
 
// Function to find the unique subsets
vector<vector<int> > solve(vector<int>& v)
{
    // To store the resulting subsets
    set<vector<int> > res;
    vector<int> subset;
    int size = v.size();
 
    // Finding 2^N
    int N = 1 << size;
    for (int i = 1; i < N; i++) {
 
        int bit = i;
        subset.clear();
        int pos = 0;
 
        while (bit) {
 
            // If the bit is set inset
            // it into the subset
            if (bit & 1) {
                subset.push_back(v[pos]);
            }
            pos++;
            bit >>= 1;
        }
        res.insert(subset);
    }
 
    vector<vector<int> > result;
    for (auto u : res)
        result.push_back(u);
    return result;
}
 
// Driver code
int main()
{
    vector<int> A = { 1, 2, 2 };
 
    // Function call
    vector<vector<int> > result = solve(A);
    for (auto v : result) {
        for (int i : v) {
            cout << i << " ";
        }
        cout << "\n";
    }
    return 0;
}


Output

1 
1 2 
1 2 2 
2 
2 2 

Time Complexity: O(N2 * 2N)
Auxiliary Space: O(2N)


My Personal Notes arrow_drop_up
Recommended Articles
Page :

Start Your Coding Journey Now!