Longest Bitonic Subsequence
Solve this ProblemTest Case 1:
Test Case 2:
Test Case 3:
Constraints
- ◆
1 ≤ nums.length ≤ 12 - ◆
-1000 ≤ nums[i] ≤ 1000
Try the Dry Run
Don't just read the solution — watch it execute, one step at a time.
Approach & Solutions
Brute Force — Recursive Without Memoization
BruteA bitonic sequence is an increasing run followed by a decreasing run that share exactly one peak element. So for every index, treating it as that peak and separately asking two questions — how long is the longest strictly increasing run that can end exactly here, and how long is the longest strictly decreasing run that can start exactly here — is enough: adding those two lengths together double-counts the peak itself, so subtracting 1 gives the bitonic length through that peak. Both of those sub-questions are solved the same way the plain Longest Increasing Subsequence recursion solves it — skip an unhelpful element or extend through it — just run once forward and once backward, with nothing cached between calls.
O(2^n)O(n)1class Solution {
2 private int[] nums;
3
4 public int longestBitonicSubsequence(int[] nums) {
5 this.nums = nums;
6 int n = nums.length;
7 int best = 0;
8 for (int i = 0; i < n; i++) {
9 best = Math.max(best, incEndingAt(i) + decStartingAt(i) - 1);
10 }
11 return best;
12 }
13
14 private int incEndingAt(int i) {
15 int best = 1;
16 for (int j = 0; j < i; j++) {
17 if (nums[j] < nums[i]) {
18 best = Math.max(best, 1 + incEndingAt(j));
19 }
20 }
21 return best;
22 }
23
24 private int decStartingAt(int i) {
25 int best = 1;
26 for (int j = i + 1; j < nums.length; j++) {
27 if (nums[j] < nums[i]) {
28 best = Math.max(best, 1 + decStartingAt(j));
29 }
30 }
31 return best;
32 }
33}Optimal — Two-Pass 1D DP
OptimalCompute the same two quantities as arrays instead of recursive calls. inc[i] is filled by a forward pass exactly like the plain LIS table: for each index, check every earlier index and extend whichever gives the longest increasing run ending here. dec[i] is filled by a mirror-image backward pass: for each index, check every later index and extend whichever gives the longest decreasing run starting here. With both tables complete, sweeping once more and taking inc[i] + dec[i] - 1 at every index — subtracting 1 so the shared peak isn't counted twice — and keeping the largest value found gives the answer.
O(n²)O(n)1class Solution {
2 public int longestBitonicSubsequence(int[] nums) {
3 int n = nums.length;
4 int[] inc = new int[n];
5 int[] dec = new int[n];
6 Arrays.fill(inc, 1);
7 Arrays.fill(dec, 1);
8 for (int i = 1; i < n; i++) {
9 for (int j = 0; j < i; j++) {
10 if (nums[j] < nums[i]) {
11 inc[i] = Math.max(inc[i], inc[j] + 1);
12 }
13 }
14 }
15 for (int i = n - 2; i >= 0; i--) {
16 for (int j = i + 1; j < n; j++) {
17 if (nums[j] < nums[i]) {
18 dec[i] = Math.max(dec[i], dec[j] + 1);
19 }
20 }
21 }
22 int best = 0;
23 for (int i = 0; i < n; i++) {
24 best = Math.max(best, inc[i] + dec[i] - 1);
25 }
26 return best;
27 }
28}