BackmediumDynamic Programminguncategorizedmedium

Non Descending Subsequence Length Solution

Problem Statement

Given an array of integers values, determine the length of the longest non-decreasing subsequence. A subsequence is derived by deleting zero or more elements from the original array without changing the relative order of the remaining elements. The subsequence is considered non-decreasing if, for every pair of consecutive elements in the subsequence, the later element is greater than or equal to the earlier one.

Your task is to compute the maximum possible length of such a subsequence. Note that the elements in the subsequence do not need to be contiguous in the original array, but their indices must be strictly increasing.

Return the integer representing the length of the longest non-decreasing subsequence.

Example 1
Input
values = [10, 9, 2, 5, 3, 7, 101, 18]
Output
4

Explanation: The longest non-decreasing subsequence is [2, 3, 7, 101] or [2, 5, 7, 101]. Both have length 4. Another valid subsequence is [9, 10, 101] with length 3, which is shorter. The subsequence [10, 101] has length 2. The maximum length found is 4.

Example 2
Input
values = [0, 1, 0, 3, 2, 3]
Output
4

Explanation: One of the longest non-decreasing subsequences is [0, 0, 2, 3] (indices 0, 2, 4, 5) or [0, 1, 2, 3] (indices 0, 1, 4, 5). Both have length 4. The subsequence [0, 1, 3, 3] (indices 0, 1, 3, 5) also has length 4. The maximum length is 4.

Example 3
Input
values = [7, 7, 7, 7, 7]
Output
5

Explanation: Since all elements are equal, the entire array forms a non-decreasing subsequence. The length of the array is 5, so the answer is 5.

Example 4
Input
values = [5, 4, 3, 2, 1]
Output
1

Explanation: The array is strictly decreasing. No two elements can form a non-decreasing pair because each subsequent element is smaller than the previous one. Therefore, the longest non-decreasing subsequence consists of a single element, and the length is 1.

Constraints

  • 1 <= values.length <= 10^5
  • -10^9 <= values[i] <= 10^9
Live Compiler1 Free Run Available
Loading Editor...
Test Cases & Output
Click "Run" to test your 1 free compile trial!

🚀 Practice this problem

Run code, get AI hints & track streak

Sign Up Free

Non Descending Subsequence Length — Problem Statement & Solution Guide

Dynamic ProgrammingMediumMixed
TimeO(n log n)
|
SpaceO(n)

Problem Description

Given an array of integers values, determine the length of the longest non-decreasing subsequence. A subsequence is derived by deleting zero or more elements from the original array without changing the relative order of the remaining elements. The subsequence is considered non-decreasing if, for every pair of consecutive elements in the subsequence, the later element is greater than or equal to the earlier one.

Your task is to compute the maximum possible length of such a subsequence. Note that the elements in the subsequence do not need to be contiguous in the original array, but their indices must be strictly increasing.

Return the integer representing the length of the longest non-decreasing subsequence.

DSA Pattern Breakdown

DSA Pattern Breakdown

"Non Descending Subsequence Length"

medium

WHY DOES IT MATTER?

The LNDS pattern exemplifies how a seemingly quadratic DP problem can be reduced to logarithmic time by recognizing that only the minimal tail values matter for future extensions. This pattern is widely applicable to sequence problems, such as longest increasing subsequence, longest common subsequence with constraints, and even certain scheduling problems where order matters but exact values can be abstracted away.

OPTIMIZATION CHALLENGE

The core insight is that the exact subsequence is irrelevant; only the minimal possible tail for each length matters. By maintaining a tails array and using binary search, we avoid examining all previous elements, cutting the time from O(n^2) to O(n log n).

REAL-WORLD CONNECTION

Imagine a warehouse where items are stacked in non‑decreasing order of weight to avoid crushing. The LNDS algorithm is akin to a robot that, as it scans each item, decides whether to place it on an existing stack (if it fits) or start a new stack, always keeping track of the lightest possible top item for each stack height. This ensures the robot can quickly determine the maximum number of items that can be safely stacked without re‑ordering.

When explaining this in an interview, emphasize the "patience sorting" analogy and explicitly state the change from upper_bound to lower_bound for non‑decreasing sequences. Also, be ready to discuss edge cases like all equal elements or strictly decreasing inputs, as they test your understanding of the algorithm’s nuances.

COMPLEXITY AT A GLANCE

⏱ Time:O(n log n)
đŸ’Ÿ Space:O(n)

Core Theory — Why This Approach?

The problem of finding the longest non‑decreasing subsequence (LNDS) is a classic dynamic programming challenge. A naive solution examines every pair of indices, building a DP array where dp[i] stores the length of the longest LNDS ending at position i. This yields an O(n^2) time complexity and O(n) space, which quickly becomes infeasible when n reaches 10^5 or more, as the number of pairwise comparisons explodes.

The optimal paradigm transforms the problem into a variant of patience sorting, leveraging binary search to maintain an auxiliary array, often called "tails", where tails[k] holds the smallest possible tail value of a non‑decreasing subsequence of length k+1. By iterating through the input and performing a binary search on tails, we can update the array in O(log n) time per element. This reduces the overall complexity to O(n log n) while keeping space linear. The key insight is that the exact values of intermediate subsequences are irrelevant; only the minimal possible tail matters for extending future elements.

Because the LNDS problem is a special case of the Longest Increasing Subsequence (LIS) with the allowance of equal elements, the same algorithm applies with a slight modification: use "lower_bound" (first element >= current) instead of "upper_bound" (first element > current) when performing the binary search. This subtle change ensures that equal values can be appended, preserving the non‑decreasing property without inflating the subsequence length unnecessarily.

Interview Questions on This Problem

Q1How would you modify the classic LIS algorithm to handle non‑decreasing subsequences, and why is this modification necessary?

In the classic LIS algorithm we use upper_bound to find the first element greater than the current value, ensuring strictly increasing sequences. For non‑decreasing subsequences we replace upper_bound with lower_bound, which finds the first element greater than or equal to the current value. This allows equal elements to be appended, maintaining the non‑decreasing property while still keeping the tails array minimal.

Q2A fintech platform needs to process a stream of transaction amounts and quickly report the length of the longest non‑decreasing subsequence seen so far. What data structure would you use to support real‑time updates, and how would you handle deletions?

Use a balanced binary search tree (e.g., AVL or Red‑Black) or a Fenwick tree to maintain the tails array dynamically. Each insertion updates the tails array in O(log n). Deletions are more complex; one approach is to maintain a multiset of values and recompute tails lazily or use a segment tree that supports range minimum queries to reconstruct the LNDS after deletions.

Q3During an interview at a high‑growth startup, you’re asked to explain why an O(n^2) DP solution would be unacceptable for an input size of 200,000. What performance metrics would you cite?

For n=200,000, an O(n^2) algorithm would perform roughly 4×10^10 operations, which would take many minutes or hours even on a modern CPU. In contrast, an O(n log n) solution would require about 200,000×log2(200,000) ≈ 3.4 million operations, completing in milliseconds. The startup’s real‑time analytics pipeline demands sub‑second latency, making the quadratic approach infeasible.

Examples

Example 1

Input

values = [10, 9, 2, 5, 3, 7, 101, 18]

Output

4

Explanation: The longest non-decreasing subsequence is [2, 3, 7, 101] or [2, 5, 7, 101]. Both have length 4. Another valid subsequence is [9, 10, 101] with length 3, which is shorter. The subsequence [10, 101] has length 2. The maximum length found is 4.

Example 2

Input

values = [0, 1, 0, 3, 2, 3]

Output

4

Explanation: One of the longest non-decreasing subsequences is [0, 0, 2, 3] (indices 0, 2, 4, 5) or [0, 1, 2, 3] (indices 0, 1, 4, 5). Both have length 4. The subsequence [0, 1, 3, 3] (indices 0, 1, 3, 5) also has length 4. The maximum length is 4.

Example 3

Input

values = [7, 7, 7, 7, 7]

Output

5

Explanation: Since all elements are equal, the entire array forms a non-decreasing subsequence. The length of the array is 5, so the answer is 5.

Example 4

Input

values = [5, 4, 3, 2, 1]

Output

1

Explanation: The array is strictly decreasing. No two elements can form a non-decreasing pair because each subsequent element is smaller than the previous one. Therefore, the longest non-decreasing subsequence consists of a single element, and the length is 1.

Constraints

  • 1 <= values.length <= 10^5
  • -10^9 <= values[i] <= 10^9

Optimal Approach & Strategy

Maintain a tails array of minimal tails for each subsequence length and update it via binary search for each element, achieving O(n log n) time and O(n) space.

Brute Force Approach

Check every pair of indices to build a DP array where dp[i] is the longest subsequence ending at i. This takes O(n^2) time and O(n) space.

Verified Code Solutions

JavaScript Solution
Time: O(n log n)
function solution(nums) {
       if (nums.length === 0) {
           return 0;
       }
       let maxLen = 1;
       let currLen = 1;
       for (let i = 1; i < nums.length; i++) {
           if (nums[i] >= nums[i - 1]) {
               currLen++;
               maxLen = Math.max(maxLen, currLen);
           } else {
               currLen = 1;
           }
       }
       return maxLen;
   }

Asked in Top Tech Interviews

uncategorizedmediumnone

Solve in Interative Editor

Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.