🧠
Sorting + Longest Increasing Subsequence (LIS) O(n²)
💡 By sorting envelopes and then applying LIS on heights, we reduce complexity conceptually. This approach bridges brute force and optimal solutions.
Intuition
Sort envelopes by width ascending and height ascending, then find LIS on heights where heights must strictly increase.
Algorithm
- Sort envelopes by width ascending and height ascending.
- Extract the heights into a separate array.
- Find the LIS on the heights array using O(n²) DP.
- Return the length of the LIS as the maximum nesting.
💡 Separating width and height after sorting simplifies the problem to a classic LIS, easier to understand than nested loops on pairs.
from typing import List
def maxEnvelopes(envelopes: List[List[int]]) -> int:
envelopes.sort(key=lambda x: (x[0], x[1]))
heights = [h for _, h in envelopes]
n = len(heights)
dp = [1] * n
for i in range(n):
for j in range(i):
if heights[j] < heights[i]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp) if dp else 0
# Driver code
if __name__ == '__main__':
test = [[5,4],[6,4],[6,7],[2,3]]
print(maxEnvelopes(test))
Line Notes
envelopes.sort(key=lambda x: (x[0], x[1]))Sort envelopes by width then height to prepare for LIS on heights
heights = [h for _, h in envelopes]Extract heights to reduce problem to LIS on one dimension
dp = [1] * nInitialize dp for LIS with 1 for each height as minimal subsequence length
if heights[j] < heights[i]:Check strictly increasing condition for LIS to build valid sequences
import java.util.*;
public class Solution {
public int maxEnvelopes(int[][] envelopes) {
Arrays.sort(envelopes, (a, b) -> {
if (a[0] != b[0]) return a[0] - b[0];
else return a[1] - b[1];
});
int n = envelopes.length;
int[] heights = new int[n];
for (int i = 0; i < n; i++) heights[i] = envelopes[i][1];
int[] dp = new int[n];
Arrays.fill(dp, 1);
int max = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (heights[j] < heights[i]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
max = Math.max(max, dp[i]);
}
return max;
}
public static void main(String[] args) {
Solution sol = new Solution();
int[][] test = {{5,4},{6,4},{6,7},{2,3}};
System.out.println(sol.maxEnvelopes(test));
}
}
Line Notes
Arrays.sort(envelopes, (a, b) -> {Sort envelopes by width then height for LIS preparation
int[] heights = new int[n];Extract heights to apply LIS on one dimension
Arrays.fill(dp, 1);Initialize dp for LIS with 1 as minimal subsequence length
if (heights[j] < heights[i]) {Check strictly increasing condition for LIS to build valid sequences
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int maxEnvelopes(vector<vector<int>>& envelopes) {
sort(envelopes.begin(), envelopes.end(), [](const vector<int>& a, const vector<int>& b) {
if (a[0] != b[0]) return a[0] < b[0];
return a[1] < b[1];
});
int n = envelopes.size();
vector<int> heights(n);
for (int i = 0; i < n; i++) heights[i] = envelopes[i][1];
vector<int> dp(n, 1);
int max_len = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (heights[j] < heights[i]) {
dp[i] = max(dp[i], dp[j] + 1);
}
}
max_len = max(max_len, dp[i]);
}
return max_len;
}
int main() {
vector<vector<int>> test = {{5,4},{6,4},{6,7},{2,3}};
cout << maxEnvelopes(test) << endl;
return 0;
}
Line Notes
sort(envelopes.begin(), envelopes.end(), [](const vector<int>& a, const vector<int>& b) {Sort envelopes by width then height for LIS preparation
vector<int> heights(n);Extract heights for LIS on one dimension
vector<int> dp(n, 1);Initialize dp for LIS with 1 as minimal subsequence length
if (heights[j] < heights[i]) {Check strictly increasing condition for LIS to build valid sequences
var maxEnvelopes = function(envelopes) {
envelopes.sort((a, b) => a[0] - b[0] || a[1] - b[1]);
const heights = envelopes.map(e => e[1]);
const n = heights.length;
const dp = new Array(n).fill(1);
let maxLen = 1;
for (let i = 0; i < n; i++) {
for (let j = 0; j < i; j++) {
if (heights[j] < heights[i]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
maxLen = Math.max(maxLen, dp[i]);
}
return maxLen;
};
// Test
console.log(maxEnvelopes([[5,4],[6,4],[6,7],[2,3]]));
Line Notes
envelopes.sort((a, b) => a[0] - b[0] || a[1] - b[1]);Sort envelopes by width then height for LIS preparation
const heights = envelopes.map(e => e[1]);Extract heights for LIS on one dimension
const dp = new Array(n).fill(1);Initialize dp for LIS with 1 as minimal subsequence length
if (heights[j] < heights[i]) {Check strictly increasing condition for LIS to build valid sequences
Sorting is O(n log n), LIS with nested loops is O(n²), DP array uses O(n) space.
💡 For n=20, this means about 400 comparisons, better than brute force but still slow for large n.
Interview Verdict: Accepted but slow for large inputs
This approach is a conceptual improvement but still not efficient enough for very large inputs.