Talentd
FleetCode
Make and follow sheets.Sign in

Non-decreasing Subsequences solution and explanation

MediumArrayBacktrackingBit ManipulationHash Table5 min read63.1% acceptanceUpdated 9 Apr 2026
Non-decreasing Subsequences | Khaandani Backtracking Template | Explanation | Live CodingcodestorywithMIK · 29:42 · 19K viewsWatch on YouTube
9 more videos on this problem
  1. Non-decreasing Subsequences | Khaandani Backtracking Template | Explanation | Live CodingcodestorywithMIK · 29:42 · 19K views
  2. LeetCode 491 Non Decreasing SubsequenceJeevan Kumar - Cracckify · 7:54 · 3K views
  3. 491. Non-decreasing Subsequences | LeetCodeDeep Tech · 12:45 · 2K views
  4. Leetcode 491. Non-decreasing Subsequences | CodingDecoded SDE sheetCoding Decoded · 5:49 · 1K views
  5. Leetcode 491: Non-decreasing SubsequencesAlgorithms Casts · 12:39 · 1K views
  6. Non-decreasing Subsequences | Leetcode - 491 | Python | Coding InterviewTechError · 7:26 · 969 views
  7. Leetcode 491 Non-decreasing Subsequences (Java)TheAnalyticGuy · 9:33 · 557 views
  8. Leetcode 491: Non-decreasing Subsequences | Master Backtracking | Intuition and ApproachCode Concepts with Animesh · 25:03 · 515 views
  9. 491. Non-decreasing Subsequences | Backtracking + Bit Manipulation | LeetCode MediumJust enjoy the journey · 25:05 · 455 views
  10. 491. Non-decreasing Subsequences - LEETCODECode Crusaders · 8:00 · 329 views

The problem

Given an integer array nums, return all the different possible non-decreasing subsequences of the given array with at least two elements. You may return the answer in any order.

Example 1:

Input: nums = [4,6,7,7]
Output: [[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

Example 2:

Input: nums = [4,4,3,2,1]
Output: [[4,4]]

Constraints:

  • 1 <= nums.length <= 15
  • -100 <= nums[i] <= 100

Approach overview

Problem Overview: Given an integer array nums, return all different subsequences where the elements are in non-decreasing order and the subsequence length is at least 2. The challenge is not generating subsequences—that part is straightforward—but removing duplicates while preserving the non-decreasing constraint.

Approach 1: Recursive Backtracking with Set Pruning (O(2^n) time, O(n) recursion space)

This approach explores the subsequence decision tree using backtracking. At each index, you decide whether to include the current element in the growing subsequence. You only add a number if it keeps the sequence non-decreasing (nums[i] >= last_element). Duplicate subsequences are avoided by maintaining a set at each recursion level that tracks which values were already used at that depth. This prevents generating identical branches when the input contains repeated numbers. The algorithm iterates forward from the current index, recursively building sequences, and records results whenever the subsequence length reaches at least two. Time complexity is O(2^n) since every element can be included or skipped, and recursion stack space is O(n) while storing results may take additional space depending on output size.

Approach 2: Iterative Enumeration with Bitmasking (O(n * 2^n) time, O(n) space)

Another way to generate subsequences is by representing each subset with a binary mask using bit manipulation. For an array of length n, iterate from 0 to (1 << n) - 1. Each bitmask indicates which elements belong to the subsequence. While constructing the subsequence, verify two conditions: the length must be at least two and the numbers must be non-decreasing. To avoid duplicates caused by repeated values in the array, store generated subsequences in a hash-based structure such as a set, leveraging concepts from hash table deduplication. This approach is easy to implement and useful for understanding how subset enumeration works. However, validating order and deduplicating results adds overhead, resulting in O(n * 2^n) time complexity.

Recommended for interviews: Recursive backtracking with a per-level set is the expected solution. Interviewers want to see that you can generate subsequences while pruning duplicates efficiently. The brute-force bitmask approach demonstrates understanding of subset enumeration, but the backtracking solution shows stronger control of recursion, pruning, and state management—skills commonly evaluated in problems involving arrays and combinatorial search.

Approach 1: Recursive Backtracking

This approach leverages a recursive backtracking method to explore all possible subsequences. The process involves recursively adding elements to the current subsequence and proceeding to the next elements only if they maintain a non-decreasing order. This approach ensures each subsequence is visited and non-decreasing ones are retained. To avoid duplication, a set or unique list is used for storing resulting subsequences.

from typing import List

def findSubsequences(nums: List[int]) -> List[List[int]]:
    def backtrack(start, path):
        if len(path) > 1:
            res.add(tuple(path))
        for i in range(start, len(nums)):
            if not path or path[-1] <= nums[i]:
                backtrack(i + 1, path + [nums[i]])

    res = set()
    backtrack(0, [])
    return [list(seq) for seq in res]

# Example usage
nums = [4,6,7,7]
print(findSubsequences(nums))
#include <vector>
#include <set>

using namespace std;

void backtrack(int start, vector<int>& nums, vector<int>& path, set<vector<int>>& res) {
    if (path.size() > 1) {
        res.insert(path);
    }
    for (int i = start; i < nums.size(); ++i) {
        if (path.empty() || path.back() <= nums[i]) {
            path.push_back(nums[i]);
            backtrack(i + 1, nums, path, res);
            path.pop_back();
        }
    }
}

vector<vector<int>> findSubsequences(vector<int>& nums) {
    set<vector<int>> res;
    vector<int> path;
    backtrack(0, nums, path, res);
    return vector<vector<int>>(res.begin(), res.end());
}

// Example usage
// vector<int> nums = {4,6,7,7};
// vector<vector<int>> result = findSubsequences(nums);

The code defines a recursive function `backtrack` that explores all potential subsequences starting at each index of the array. It uses a set `res` to avoid duplicates. Subsequences are formed by gradually building and checking them to ensure they are non-decreasing before adding.

Complexity

Time Complexity: O(2^n), where n is the number of elements in the array, since each element can either be included or not in a subsequence.

Space Complexity: O(n*2^n) for storing all subsequences in the worst case.

Try this approach in the editor

Approach 2: Iterative with Bitmasking

This approach utilizes a bitmask technique to generate all possible subsequences iteratively. Each number's presence in a given subsequence is represented by a corresponding bit in a binary number, allowing for a systematic generation of potential subsequences. By iterating over all possible binary representations, the algorithm constructs and evaluates subsequences for their non-decreasing nature before storing them.

import java.util.*;

public class Solution {
    public List<List<Integer>> findSubsequences(int[] nums) {
        Set<List<Integer>> result = new HashSet<>();
        int n = nums.length;

        for (int mask = 1; mask < (1 << n); mask++) {
            List<Integer> sequence = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                if ((mask & (1 << i)) != 0) {
                    if (sequence.isEmpty() || sequence.get(sequence.size() - 1) <= nums[i]) {
                        sequence.add(nums[i]);
                    } else {
                        sequence.clear();
                        break;
                    }
                }
            }
            if (sequence.size() > 1) {
                result.add(sequence);
            }
        }
        return new ArrayList<>(result);
    }

    public static void main(String[] args) {
        Solution sol = new Solution();
        int[] nums = {4,6,7,7};
        System.out.println(sol.findSubsequences(nums));
    }
}
var findSubsequences = function(nums) {
    const result = new Set();
    const n = nums.length;

    for (let mask = 1; mask < (1 << n); mask++) {
        const sequence = [];
        for (let i = 0; i < n; i++) {
            if (mask & (1 << i)) {
                if (!sequence.length || sequence[sequence.length - 1] <= nums[i]) {
                    sequence.push(nums[i]);
                } else {
                    break;
                }
            }
        }
        if (sequence.length > 1) {
            result.add(sequence.toString());
        }
    }
    return Array.from(result).map(seq => seq.split(",").map(Number));
};

// Example usage
// console.log(findSubsequences([4,6,7,7]));

The Java implementation uses bitmasking to generate subsequences, with a `HashSet` to handle duplicates. Each bit in the mask corresponds to an element, and their presence in each subsequence is determined iteratively. Valid subsequences are added to the result after ensuring the non-decreasing order.

Complexity

Time Complexity: O(n * 2^n) for generating and checking subsequences.

Space Complexity: O(n * 2^n) due to the result storage.

Try this approach in the editor

Approach 3: Default Approach

func findSubsequences(nums []int) [][]int {
	var ans [][]int
	var dfs func(u, last int, t []int)
	dfs = func(u, last int, t []int) {
		if u == len(nums) {
			if len(t) > 1 {
				ans = append(ans, slices.Clone(t))
			}
			return
		}
		if nums[u] >= last {
			t = append(t, nums[u])
			dfs(u+1, nums[u], t)
			t = t[:len(t)-1]
		}
		if nums[u] != last {
			dfs(u+1, last, t)
		}
	}
	var t []int
	dfs(0, -1000, t)
	return ans
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Recursive Backtracking

Time Complexity: O(2^n), where n is the number of elements in the array, since each element can either be included or not in a subsequence.

Space Complexity: O(n*2^n) for storing all subsequences in the worst case.

Iterative with Bitmasking

Time Complexity: O(n * 2^n) for generating and checking subsequences.

Space Complexity: O(n * 2^n) due to the result storage.

Default Approach–

Complexity analysis

ApproachTimeSpaceWhen to Use
Recursive Backtracking with SetO(2^n)O(n)Best general solution. Efficient duplicate pruning and commonly expected in interviews.
Iterative Bitmask EnumerationO(n * 2^n)O(n)Good for understanding subset generation or when implementing iterative solutions.

Frequently asked questions

Is Non-decreasing Subsequences easy or hard?

Non-decreasing Subsequences is considered a medium-level problem. Generating subsequences is straightforward, but handling duplicates while preserving order introduces additional complexity. The difficulty mainly comes from managing recursion state and pruning duplicate branches efficiently.

How to solve Non-decreasing Subsequences in O(n)?

An O(n) algorithm is not possible because the number of valid subsequences can grow exponentially with the input size. Any correct algorithm must potentially enumerate up to 2^n subsequences. Efficient solutions focus on pruning invalid branches early using backtracking and hash sets to avoid generating duplicates.

What is the best approach for Non-decreasing Subsequences?

Recursive backtracking with a per-level hash set is the most efficient and commonly accepted solution. The algorithm builds subsequences incrementally while ensuring each next value keeps the sequence non-decreasing. A set at each recursion depth prevents duplicate subsequences when the input array contains repeated numbers. The overall complexity is O(2^n) time with O(n) recursion space.

Is Non-decreasing Subsequences asked at Google/Amazon/Meta?

Non-decreasing subsequence generation problems appear in interviews at companies like Amazon, Meta, and Google as variations of backtracking and subset generation. The problem tests recursion, duplicate pruning, and sequence validation—skills commonly evaluated in medium-level algorithm interviews.

What data structure is used in Non-decreasing Subsequences?

The main data structures include arrays for traversal, recursion stacks for backtracking, and hash sets to prevent duplicate subsequences. The set is typically used at each recursion level to ensure the same value is not chosen multiple times from the same depth of the search tree.

What is the time complexity of Non-decreasing Subsequences?

The typical backtracking solution runs in O(2^n) time because each element can either be included or skipped when generating subsequences. Additional checks ensure the subsequence remains non-decreasing and duplicates are avoided. Space complexity is O(n) for the recursion stack, excluding the space required to store resulting subsequences.

Non-decreasing Subsequences Python or Java solution approach?

Python and Java implementations usually rely on recursive backtracking. The algorithm maintains a current path list, checks if the next number keeps the sequence non-decreasing, and uses a set at each recursion depth to skip duplicates. JavaScript and Java variants sometimes demonstrate a bitmask enumeration approach as an alternative.

Comments

Now write it yourselfClose this page and solve Non-decreasing Subsequences in the editor, without looking.
Practise Non-decreasing Subsequences