🧠
Brute Force (Pure Recursion)
💡 This approach helps understand the problem's recursive nature and why naive solutions are inefficient, motivating DP.
Intuition
Try every subsequence by recursively deciding to include or exclude each element, and track the longest increasing subsequence found.
Algorithm
- Start from the first element and recursively explore subsequences including or excluding it.
- At each step, keep track of the previous element included to ensure increasing order.
- Return the maximum length found among all recursive paths.
- Combine results to find the overall longest increasing subsequence.
💡 The recursion tree grows exponentially because each element has two choices, making it hard to track without memoization.
Recurrence:LIS(i, prev) = max(1 + LIS(i+1, i) if nums[i] > nums[prev], LIS(i+1, prev))
def lengthOfLIS(nums):
def dfs(i, prev_index):
if i == len(nums):
return 0
taken = 0
if prev_index == -1 or nums[i] > nums[prev_index]:
taken = 1 + dfs(i + 1, i)
not_taken = dfs(i + 1, prev_index)
return max(taken, not_taken)
return dfs(0, -1)
# Example usage
if __name__ == '__main__':
print(lengthOfLIS([10,9,2,5,3,7,101,18])) # Output: 4
Line Notes
def dfs(i, prev_index):Defines recursive helper to explore subsequences starting at i with prev_index as last included element
if i == len(nums):Base case: reached end of array, no more elements to include
if prev_index == -1 or nums[i] > nums[prev_index]:Check if current element can be included to maintain increasing order
return max(taken, not_taken)Choose the better option between including or skipping current element
public class Solution {
public int lengthOfLIS(int[] nums) {
return dfs(nums, 0, -1);
}
private int dfs(int[] nums, int i, int prevIndex) {
if (i == nums.length) return 0;
int taken = 0;
if (prevIndex == -1 || nums[i] > nums[prevIndex]) {
taken = 1 + dfs(nums, i + 1, i);
}
int notTaken = dfs(nums, i + 1, prevIndex);
return Math.max(taken, notTaken);
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.lengthOfLIS(new int[]{10,9,2,5,3,7,101,18})); // 4
}
}
Line Notes
public int lengthOfLIS(int[] nums)Entry point calling recursive dfs starting at index 0 with no previous element
if (i == nums.length) return 0;Base case: no elements left to process
if (prevIndex == -1 || nums[i] > nums[prevIndex])Check if current element can be included to keep subsequence increasing
return Math.max(taken, notTaken);Return the best choice between including or skipping current element
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
int dfs(const vector<int>& nums, int i, int prevIndex) {
if (i == nums.size()) return 0;
int taken = 0;
if (prevIndex == -1 || nums[i] > nums[prevIndex]) {
taken = 1 + dfs(nums, i + 1, i);
}
int notTaken = dfs(nums, i + 1, prevIndex);
return max(taken, notTaken);
}
int lengthOfLIS(vector<int>& nums) {
return dfs(nums, 0, -1);
}
};
int main() {
Solution sol;
vector<int> nums = {10,9,2,5,3,7,101,18};
cout << sol.lengthOfLIS(nums) << endl; // 4
return 0;
}
Line Notes
int dfs(const vector<int>& nums, int i, int prevIndex)Recursive helper exploring subsequences starting at i with prevIndex as last included element
if (i == nums.size()) return 0;Base case: no more elements to consider
if (prevIndex == -1 || nums[i] > nums[prevIndex])Include current element if it maintains increasing order
return max(taken, notTaken);Choose the better option between including or skipping current element
function lengthOfLIS(nums) {
function dfs(i, prevIndex) {
if (i === nums.length) return 0;
let taken = 0;
if (prevIndex === -1 || nums[i] > nums[prevIndex]) {
taken = 1 + dfs(i + 1, i);
}
let notTaken = dfs(i + 1, prevIndex);
return Math.max(taken, notTaken);
}
return dfs(0, -1);
}
// Example usage
console.log(lengthOfLIS([10,9,2,5,3,7,101,18])); // 4
Line Notes
function dfs(i, prevIndex)Recursive helper to explore subsequences starting at i with prevIndex as last included element
if (i === nums.length) return 0;Base case: no more elements to process
if (prevIndex === -1 || nums[i] > nums[prevIndex])Check if current element can be included to maintain increasing order
return Math.max(taken, notTaken);Return the best choice between including or skipping current element
TimeO(2^n)
SpaceO(n) recursion stack
Each element has two choices (include or exclude), leading to exponential growth in calls.
💡 For n=20, this means over a million calls, which is impractical for interviews.
Interview Verdict: TLE
This approach is too slow for large inputs but is essential to understand the problem's structure.