Given an array arr[] of non-negative integers, find the subsequence of length 3 with the maximum product such that the elements of the subsequence are in strictly increasing order. Return the 3 elements as a list. If no such subsequence exists, return an empty list.
Examples:
Input: arr[] = [6, 7, 8, 1, 2, 3, 9, 10]
Output: 8 9 10
Explanation: 3 increasing elements of the given arrays are 8,9 and 10 which forms the subsequence of size 3 with maximum product.Input: arr[] = [3, 4, 2, 1]
Output: []
Explanation: No strictly increasing subsequence of length 3 exists, so an empty list is returned.
Table of Content
[Naive Approach] Using Brute Force - O(n^3) Time and O(1) Space
Try all possible triplets (i, j, k) where i < j < k. For each triplet check if elements are in strictly increasing order. If yes compute the product and update the maximum. Return the triplet with maximum product.
- For every triplet i < j < k check if arr[i] < arr[j] < arr[k].
- Compute product arr[i] * arr[j] * arr[k] and update result if greater than current maximum.
- If no valid triplet found return empty list.
#include <bits/stdc++.h>
using namespace std;
vector<int> maxProductSubsequence(vector<int>& arr) {
int n = arr.size();
long long maxProd = -1;
vector<int> res;
// Try all triplets i < j < k with arr[i] < arr[j] < arr[k]
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
for (int k = j + 1; k < n; k++) {
if (arr[i] < arr[j] && arr[j] < arr[k]) {
long long prod = (1LL * arr[i]) * arr[j] * arr[k];
if (prod > maxProd) {
maxProd = prod;
res = {arr[i], arr[j], arr[k]};
}
}
}
}
}
return res;
}
int main() {
vector<int> arr = {6, 7, 8, 1, 2, 3, 9, 10};
vector<int> res = maxProductSubsequence(arr);
for (int x : res) cout << x << " ";
return 0;
}
import java.util.*;
class GfG {
static List<Integer> maxProductSubsequence(int[] arr) {
int n = arr.length;
long maxProd = -1;
List<Integer> res = new ArrayList<>();
// Try all triplets i < j < k with arr[i] < arr[j] < arr[k]
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
for (int k = j + 1; k < n; k++) {
if (arr[i] < arr[j] && arr[j] < arr[k]) {
long prod = (long) arr[i] * arr[j] * arr[k];
if (prod > maxProd) {
maxProd = prod;
res = Arrays.asList(arr[i], arr[j], arr[k]);
}
}
}
}
}
return res;
}
public static void main(String[] args) {
int[] arr = {6, 7, 8, 1, 2, 3, 9, 10};
System.out.println(maxProductSubsequence(arr));
}
}
def maxProductSubsequence(arr):
n = len(arr)
maxProd = -1
res = []
# Try all triplets i < j < k with arr[i] < arr[j] < arr[k]
for i in range(n):
for j in range(i + 1, n):
for k in range(j + 1, n):
if arr[i] < arr[j] < arr[k]:
prod = arr[i] * arr[j] * arr[k]
if prod > maxProd:
maxProd = prod
res = [arr[i], arr[j], arr[k]]
return res
if __name__ == "__main__":
arr = [6, 7, 8, 1, 2, 3, 9, 10]
print(maxProductSubsequence(arr))
using System;
using System.Collections.Generic;
class GfG {
static List<int> maxProductSubsequence(int[] arr) {
int n = arr.Length;
long maxProd = -1;
List<int> res = new List<int>();
// Try all triplets i < j < k with arr[i] < arr[j] < arr[k]
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
for (int k = j + 1; k < n; k++) {
if (arr[i] < arr[j] && arr[j] < arr[k]) {
long prod = (long) arr[i] * arr[j] * arr[k];
if (prod > maxProd) {
maxProd = prod;
res = new List<int> {arr[i], arr[j], arr[k]};
}
}
}
}
}
return res;
}
static void Main() {
int[] arr = {6, 7, 8, 1, 2, 3, 9, 10};
Console.WriteLine(string.Join(", ", maxProductSubsequence(arr)));
}
}
function maxProductSubsequence(arr) {
const n = arr.length;
let maxProd = -1;
let res = [];
// Try all triplets i < j < k with arr[i] < arr[j] < arr[k]
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
for (let k = j + 1; k < n; k++) {
if (arr[i] < arr[j] && arr[j] < arr[k]) {
const prod = arr[i] * arr[j] * arr[k];
if (prod > maxProd) {
maxProd = prod;
res = [arr[i], arr[j], arr[k]];
}
}
}
}
}
return res;
}
// Driver code
const arr = [6, 7, 8, 1, 2, 3, 9, 10];
console.log(maxProductSubsequence(arr));
Output
2200
[Better Approach] Using Middle Element - O(n^2) Time and O(1) Space
Instead of trying all triplets, fix each element as the middle element and scan left for the largest smaller element and scan right for the largest greater element. This reduces one loop giving O(n^2).
- For each element arr[j] as middle, scan left to find largest element strictly less than arr[j].
- Scan right to find largest element strictly greater than arr[j].
- Compute product and update maximum.
- If no valid triplet found return empty list.
#include <bits/stdc++.h>
using namespace std;
vector<int> maxProductSubsequence(vector<int>& arr) {
int n = arr.size();
long long maxProd = -1;
vector<int> res;
for (int j = 1; j < n - 1; j++) {
// Find largest element strictly less than arr[j] from left
int left = -1;
for (int i = 0; i < j; i++)
if (arr[i] < arr[j])
left = max(left, arr[i]);
// Find largest element strictly greater than arr[j] from right
int right = -1;
for (int k = j + 1; k < n; k++)
if (arr[k] > arr[j])
right = max(right, arr[k]);
// Update result if valid triplet found
if (left != -1 && right != -1) {
long long prod = (1LL * left) * arr[j] * right;
if (prod > maxProd) {
maxProd = prod;
res = {left, arr[j], right};
}
}
}
return res;
}
int main() {
vector<int> arr = {6, 7, 8, 1, 2, 3, 9, 10};
vector<int> res = maxProductSubsequence(arr);
for (int x : res) cout << x << " ";
return 0;
}
import java.util.*;
class GfG {
static List<Integer> maxProductSubsequence(int[] arr) {
int n = arr.length;
long maxProd = -1;
List<Integer> res = new ArrayList<>();
for (int j = 1; j < n - 1; j++) {
// Find largest element strictly less than arr[j] from left
int left = -1;
for (int i = 0; i < j; i++)
if (arr[i] < arr[j])
left = Math.max(left, arr[i]);
// Find largest element strictly greater than arr[j] from right
int right = -1;
for (int k = j + 1; k < n; k++)
if (arr[k] > arr[j])
right = Math.max(right, arr[k]);
// Update result if valid triplet found
if (left != -1 && right != -1) {
long prod = (long) left * arr[j] * right;
if (prod > maxProd) {
maxProd = prod;
res = Arrays.asList(left, arr[j], right);
}
}
}
return res;
}
public static void main(String[] args) {
int[] arr = {6, 7, 8, 1, 2, 3, 9, 10};
System.out.println(maxProductSubsequence(arr));
}
}
def maxProductSubsequence(arr):
n = len(arr)
maxProd = -1
res = []
for j in range(1, n - 1):
# Find largest element strictly less than arr[j] from left
left = -1
for i in range(j):
if arr[i] < arr[j]:
left = max(left, arr[i])
# Find largest element strictly greater than arr[j] from right
right = -1
for k in range(j + 1, n):
if arr[k] > arr[j]:
right = max(right, arr[k])
# Update result if valid triplet found
if left != -1 and right != -1:
prod = left * arr[j] * right
if prod > maxProd:
maxProd = prod
res = [left, arr[j], right]
return res
if __name__ == "__main__":
arr = [6, 7, 8, 1, 2, 3, 9, 10]
print(maxProductSubsequence(arr))
using System;
using System.Collections.Generic;
class GfG {
static List<int> maxProductSubsequence(int[] arr) {
int n = arr.Length;
long maxProd = -1;
List<int> res = new List<int>();
for (int j = 1; j < n - 1; j++) {
// Find largest element strictly less than arr[j] from left
int left = -1;
for (int i = 0; i < j; i++)
if (arr[i] < arr[j])
left = Math.Max(left, arr[i]);
// Find largest element strictly greater than arr[j] from right
int right = -1;
for (int k = j + 1; k < n; k++)
if (arr[k] > arr[j])
right = Math.Max(right, arr[k]);
// Update result if valid triplet found
if (left != -1 && right != -1) {
long prod = (long) left * arr[j] * right;
if (prod > maxProd) {
maxProd = prod;
res = new List<int> {left, arr[j], right};
}
}
}
return res;
}
static void Main() {
int[] arr = {6, 7, 8, 1, 2, 3, 9, 10};
Console.WriteLine(string.Join(", ", maxProductSubsequence(arr)));
}
}
function maxProductSubsequence(arr) {
const n = arr.length;
let maxProd = -1;
let res = [];
for (let j = 1; j < n - 1; j++) {
// Find largest element strictly less than arr[j] from left
let left = -1;
for (let i = 0; i < j; i++)
if (arr[i] < arr[j])
left = Math.max(left, arr[i]);
// Find largest element strictly greater than arr[j] from right
let right = -1;
for (let k = j + 1; k < n; k++)
if (arr[k] > arr[j])
right = Math.max(right, arr[k]);
// Update result if valid triplet found
if (left !== -1 && right !== -1) {
const prod = left * arr[j] * right;
if (prod > maxProd) {
maxProd = prod;
res = [left, arr[j], right];
}
}
}
return res;
}
// Driver code
const arr = [6, 7, 8, 1, 2, 3, 9, 10];
console.log(maxProductSubsequence(arr));
Output
8 9 10
[Expected Approach] Using Sorted Set / Fenwick Tree - O(n log n) Time and O(n) Space
For each element as the middle element, instead of scanning left in O(n) we find the largest smaller element in O(log n) - using a sorted set in C++/Java, and a Fenwick tree over coordinate-compressed values in Python/JavaScript/(languages without a built-in sorted set). For the right side a single right to left pass tracking the running maximum gives the largest greater element in O(1).
- Traverse left to right maintaining a sorted set (or Fenwick tree). For each element query the largest element strictly less than it - that's left[i].
- Traverse right to left maintaining a running maximum. For each element if running max is greater than it - that's right[i].
- For each index as middle element compute left[i] * arr[i] * right[i] and track maximum.
- Return the triplet with maximum product.
#include <bits/stdc++.h>
using namespace std;
vector<int> maxProductSubsequence(vector<int>& arr) {
int n = arr.size();
vector<int> left(n, -1), right(n, -1);
// Find greatest smaller element on left for every index
set<int> st;
for (int i = 0; i < n; i++) {
auto it = st.lower_bound(arr[i]);
if (it != st.begin()) {
--it;
left[i] = *it;
}
st.insert(arr[i]);
}
// Find greatest element on right for every index
int maxRight = arr[n - 1];
for (int i = n - 2; i >= 0; i--) {
if (arr[i] < maxRight)
right[i] = maxRight;
maxRight = max(maxRight, arr[i]);
}
// Find maximum product using middle element
long long maxProd = -1;
vector<int> res;
for (int i = 0; i < n; i++) {
if (left[i] != -1 && right[i] != -1) {
long long prod = (1LL * left[i]) * arr[i] * right[i];
if (prod > maxProd) {
maxProd = prod;
res = {left[i], arr[i], right[i]};
}
}
}
return res;
}
int main() {
vector<int> arr = {6, 7, 8, 1, 2, 3, 9, 10};
vector<int> res = maxProductSubsequence(arr);
for (int x : res) cout << x << " ";
return 0;
}
import java.util.*;
class GfG {
static List<Integer> maxProductSubsequence(int[] arr) {
int n = arr.length;
int[] left = new int[n];
int[] right = new int[n];
Arrays.fill(left, -1);
Arrays.fill(right, -1);
// Find greatest smaller element on left for every index
TreeSet<Integer> set = new TreeSet<>();
for (int i = 0; i < n; i++) {
Integer smaller = set.lower(arr[i]);
if (smaller != null)
left[i] = smaller;
set.add(arr[i]);
}
// Find greatest element on right for every index
int maxRight = arr[n - 1];
for (int i = n - 2; i >= 0; i--) {
if (arr[i] < maxRight)
right[i] = maxRight;
maxRight = Math.max(maxRight, arr[i]);
}
// Find maximum product using middle element
long maxProd = -1;
List<Integer> res = new ArrayList<>();
for (int i = 0; i < n; i++) {
if (left[i] != -1 && right[i] != -1) {
long prod = (long) left[i] * arr[i] * right[i];
if (prod > maxProd) {
maxProd = prod;
res = Arrays.asList(left[i], arr[i], right[i]);
}
}
}
return res;
}
public static void main(String[] args) {
int[] arr = {6, 7, 8, 1, 2, 3, 9, 10};
System.out.println(maxProductSubsequence(arr));
}
}
def maxProductSubsequence(arr):
n = len(arr)
# Coordinate compress values to ranks for Fenwick tree indexing
sortedVals = sorted(set(arr))
rank = {v: i + 1 for i, v in enumerate(sortedVals)}
m = len(sortedVals)
# Fenwick tree for prefix maximum, -1 means no value seen
fen = [-1] * (m + 1)
def update(pos, val):
while pos <= m:
fen[pos] = max(fen[pos], val)
pos += pos & (-pos)
def query(pos):
res = -1
while pos > 0:
res = max(res, fen[pos])
pos -= pos & (-pos)
return res
# Find greatest smaller element on left for every index using Fenwick tree
left = [-1] * n
for i in range(n):
r = rank[arr[i]]
if r > 1:
left[i] = query(r - 1)
update(r, arr[i])
# Find greatest element on right for every index
right = [-1] * n
maxRight = arr[n - 1]
for i in range(n - 2, -1, -1):
if arr[i] < maxRight:
right[i] = maxRight
maxRight = max(maxRight, arr[i])
# Find maximum product using middle element
maxProd = -1
res = []
for i in range(n):
if left[i] != -1 and right[i] != -1:
prod = left[i] * arr[i] * right[i]
if prod > maxProd:
maxProd = prod
res = [left[i], arr[i], right[i]]
return res
if __name__ == "__main__":
arr = [6, 7, 8, 1, 2, 3, 9, 10]
print(maxProductSubsequence(arr))
using System;
using System.Collections.Generic;
class GfG {
static List<int> maxProductSubsequence(int[] arr) {
int n = arr.Length;
// Coordinate compress values to ranks for Fenwick tree indexing
List<int> sortedVals = new List<int>(new HashSet<int>(arr));
sortedVals.Sort();
Dictionary<int, int> rank = new Dictionary<int, int>();
for (int i = 0; i < sortedVals.Count; i++)
rank[sortedVals[i]] = i + 1;
int m = sortedVals.Count;
// Fenwick tree for prefix maximum, -1 means no value seen
int[] fen = new int[m + 1];
for (int i = 0; i <= m; i++) fen[i] = -1;
void Update(int pos, int val) {
while (pos <= m) {
fen[pos] = Math.Max(fen[pos], val);
pos += pos & (-pos);
}
}
int Query(int pos) {
int res = -1;
while (pos > 0) {
res = Math.Max(res, fen[pos]);
pos -= pos & (-pos);
}
return res;
}
// Find greatest smaller element on left for every index using Fenwick tree
int[] left = new int[n];
for (int i = 0; i < n; i++) {
int r = rank[arr[i]];
left[i] = (r > 1) ? Query(r - 1) : -1;
Update(r, arr[i]);
}
// Find greatest element on right for every index
int[] right = new int[n];
for (int i = 0; i < n; i++) right[i] = -1;
int maxRight = arr[n - 1];
for (int i = n - 2; i >= 0; i--) {
right[i] = (arr[i] < maxRight) ? maxRight : -1;
maxRight = Math.Max(maxRight, arr[i]);
}
// Find maximum product using middle element
long maxProd = -1;
List<int> res = new List<int>();
for (int i = 0; i < n; i++) {
if (left[i] != -1 && right[i] != -1) {
long prod = (long) left[i] * arr[i] * right[i];
if (prod > maxProd) {
maxProd = prod;
res = new List<int> { left[i], arr[i], right[i] };
}
}
}
return res;
}
static void Main() {
int[] arr = {6, 7, 8, 1, 2, 3, 9, 10};
Console.WriteLine(string.Join(", ", maxProductSubsequence(arr)));
}
}
function maxProductSubsequence(arr) {
const n = arr.length;
// Coordinate compress values to ranks for Fenwick tree indexing
const sortedVals = [...new Set(arr)].sort((a, b) => a - b);
const rank = new Map();
sortedVals.forEach((v, i) => rank.set(v, i + 1));
const m = sortedVals.length;
// Fenwick tree for prefix maximum, -1 means no value seen
const fen = new Array(m + 1).fill(-1);
const update = (pos, val) => {
while (pos <= m) {
fen[pos] = Math.max(fen[pos], val);
pos += pos & (-pos);
}
};
const query = (pos) => {
let res = -1;
while (pos > 0) {
res = Math.max(res, fen[pos]);
pos -= pos & (-pos);
}
return res;
};
// Find greatest smaller element on left for every index using Fenwick tree
const left = new Array(n).fill(-1);
for (let i = 0; i < n; i++) {
const r = rank.get(arr[i]);
if (r > 1)
left[i] = query(r - 1);
update(r, arr[i]);
}
// Find greatest element on right for every index
const right = new Array(n).fill(-1);
let maxRight = arr[n - 1];
for (let i = n - 2; i >= 0; i--) {
if (arr[i] < maxRight)
right[i] = maxRight;
maxRight = Math.max(maxRight, arr[i]);
}
// Find maximum product using middle element
let maxProd = -1;
let res = [];
for (let i = 0; i < n; i++) {
if (left[i] !== -1 && right[i] !== -1) {
const prod = left[i] * arr[i] * right[i];
if (prod > maxProd) {
maxProd = prod;
res = [left[i], arr[i], right[i]];
}
}
}
return res;
}
// Driver code
const arr = [6, 7, 8, 1, 2, 3, 9, 10];
console.log(maxProductSubsequence(arr));
Output
8 9 10