Given an array arr[] of size n containing positive integers, return the maximum length of the bitonic subarray.
A subarray arr[i...j] is considered bitonic if its elements first monotonically increase, and then monotonically decrease. Formally, there exists and index k (where i <= k <= j) such that:
- arr[i] <= arr[i+1] <= . . . <= arr[k]
- arr[k] >= arr[k+1] >= . . . >= arr[j]
Examples
Input: arr[] = [12, 4, 78, 90, 45, 23]
Output: 5
Explanation: The longest bitonic subarray is[4, 78, 90, 45, 23]. It starts increasing at4, peaks at90, and decreases to23, giving a length of 5.
Input: arr[] = [10, 20, 30, 40]
Output: 4
Explanation: The array[10, 20, 30, 40]is strictly increasing with no decreasing part, so the longest bitonic subarray is the entire array itself, giving a length of 4.
Table of Content
[Naive Approach] Using Nested Loops - O(n3) time and O(1) space
We check all possible subarrays, trying each index as a peak. If a subarray follows the bitonic pattern, we update the longest length found.
#include <bits/stdc++.h>
using namespace std;
// check if subarray arr[s...e] is bitonic
bool isBit(vector<int> &arr, int s, int e)
{
// try each index as potential peak
for (int p = s; p <= e; p++)
{
bool nd = true, ni = true;
// check non-decreasing from s to p
for (int i = s + 1; i <= p; i++)
{
if (arr[i] < arr[i - 1])
{
nd = false;
break;
}
}
// check non-increasing from p to e
for (int i = p + 1; i <= e; i++)
{
if (arr[i] > arr[i - 1])
{
ni = false;
break;
}
}
if (nd && ni)
return true;
}
return false;
}
// find the length of the longest bitonic subarray
int bitonic(vector<int> &arr)
{
int n = arr.size(), maxL = 0;
// enumerate all subarrays arr[i...j]
for (int i = 0; i < n; i++)
{
for (int j = i; j < n; j++)
{
if (isBit(arr, i, j))
maxL = max(maxL, j - i + 1);
}
}
return maxL;
}
int main()
{
vector<int> arr = {12, 4, 78, 90, 45, 23};
cout << bitonic(arr) << endl;
return 0;
}
// check if subarray arr[s...e] is bitonic
public class GfG {
public boolean isBit(int[] arr, int s, int e)
{
// try each index as potential peak
for (int p = s; p <= e; p++) {
boolean nd = true, ni = true;
// check non-decreasing from s to p
for (int i = s + 1; i <= p; i++) {
if (arr[i] < arr[i - 1]) {
nd = false;
break;
}
}
// check non-increasing from p to e
for (int i = p + 1; i <= e; i++) {
if (arr[i] > arr[i - 1]) {
ni = false;
break;
}
}
if (nd && ni)
return true;
}
return false;
}
// find the length of the longest bitonic subarray
public int bitonic(int[] arr)
{
int n = arr.length, maxL = 0;
// enumerate all subarrays arr[i...j]
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
if (isBit(arr, i, j))
maxL = Math.max(maxL, j - i + 1);
}
}
return maxL;
}
public void main(String[] args)
{
int[] arr = { 12, 4, 78, 90, 45, 23 };
System.out.println(bitonic(arr));
}
}
# check if subarray arr[s...e] is bitonic
def is_bit(arr, s, e):
# try each index as potential peak
for p in range(s, e + 1):
nd = True
ni = True
# check non-decreasing from s to p
for i in range(s + 1, p + 1):
if arr[i] < arr[i - 1]:
nd = False
break
# check non-increasing from p to e
for i in range(p + 1, e + 1):
if arr[i] > arr[i - 1]:
ni = False
break
if nd and ni:
return True
return False
# find the length of the longest bitonic subarray
def bitonic(arr):
n = len(arr)
max_l = 0
# enumerate all subarrays arr[i...j]
for i in range(n):
for j in range(i, n):
if is_bit(arr, i, j):
max_l = max(max_l, j - i + 1)
return max_l
if __name__ == "__main__":
arr = [12, 4, 78, 90, 45, 23]
print(bitonic(arr))
using System;
using System.Linq;
class GfG {
public bool IsBit(int[] arr, int s, int e)
{
// try each index as potential peak
for (int p = s; p <= e; p++) {
bool nd = true, ni = true;
// check non-decreasing from s to p
for (int i = s + 1; i <= p; i++) {
if (arr[i] < arr[i - 1]) {
nd = false;
break;
}
}
// check non-increasing from p to e
for (int i = p + 1; i <= e; i++) {
if (arr[i] > arr[i - 1]) {
ni = false;
break;
}
}
if (nd && ni)
return true;
}
return false;
}
// find the length of the longest bitonic subarray
public int bitonic(int[] arr)
{
int n = arr.Length, maxL = 0;
// enumerate all subarrays arr[i...j]
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
if (IsBit(arr, i, j))
maxL = Math.Max(maxL, j - i + 1);
}
}
return maxL;
}
public void Main()
{
int[] arr = { 12, 4, 78, 90, 45, 23 };
Console.WriteLine(bitonic(arr));
}
}
// check if subarray arr[s...e] is bitonic
function isBit(arr, s, e)
{
// try each index as potential peak
for (let p = s; p <= e; p++) {
let nd = true, ni = true;
// check non-decreasing from s to p
for (let i = s + 1; i <= p; i++) {
if (arr[i] < arr[i - 1]) {
nd = false;
break;
}
}
// check non-increasing from p to e
for (let i = p + 1; i <= e; i++) {
if (arr[i] > arr[i - 1]) {
ni = false;
break;
}
}
if (nd && ni)
return true;
}
return false;
}
// find the length of the longest bitonic subarray
function bitonic(arr)
{
const n = arr.length;
let maxL = 0;
// enumerate all subarrays arr[i...j]
for (let i = 0; i < n; i++) {
for (let j = i; j < n; j++) {
if (isBit(arr, i, j))
maxL = Math.max(maxL, j - i + 1);
}
}
return maxL;
}
const arr = [ 12, 4, 78, 90, 45, 23 ];
console.log(bitonic(arr));
Output
5
[Expected Approach] Precompute Increasing Decreasing - O(n) time and O(n) space
For solving this problem we can imagine each number as the peak of a mountain. For every element, count how many consecutive numbers before it are rising (or flat) and how many after it are falling (or flat). Add these two counts and subtract one (to avoid counting the peak twice). The longest mountain found this way is answer.
#include <bits/stdc++.h>
using namespace std;
int bitonic(vector<int> arr)
{
int n = arr.size();
// length of increasing subarray
// ending at all indexes
vector<int> inc(n);
// length of decreasing subarray
// starting at all indexes
vector<int> dec(n);
int i, max;
// length of increasing sequence
// ending at first index is 1
inc[0] = 1;
// length of increasing sequence
// starting at first index is 1
dec[n - 1] = 1;
// construct increasing sequence array
for (i = 1; i < n; i++)
inc[i] = (arr[i] >= arr[i - 1]) ? inc[i - 1] + 1 : 1;
// construct decreasing sequence array
for (i = n - 2; i >= 0; i--)
dec[i] = (arr[i] >= arr[i + 1]) ? dec[i + 1] + 1 : 1;
// find the length of
// maximum length bitonic sequence
max = inc[0] + dec[0] - 1;
for (i = 1; i < n; i++)
if (inc[i] + dec[i] - 1 > max)
max = inc[i] + dec[i] - 1;
return max;
}
int main()
{
vector<int> arr = {12, 4, 78, 90, 45, 23};
cout << bitonic(arr);
return 0;
}
class Main {
public int bitonic(int[] arr)
{
int n = arr.length;
// length of increasing subarray
// ending at all indexes
int[] inc = new int[n];
// length of decreasing subarray
// starting at all indexes
int[] dec = new int[n];
int i, max;
// length of increasing sequence
// ending at first index is 1
inc[0] = 1;
// length of increasing sequence
// starting at first index is 1
dec[n - 1] = 1;
// construct increasing sequence array
for (i = 1; i < n; i++)
inc[i] = (arr[i] >= arr[i - 1]) ? inc[i - 1] + 1
: 1;
// construct decreasing sequence array
for (i = n - 2; i >= 0; i--)
dec[i] = (arr[i] >= arr[i + 1]) ? dec[i + 1] + 1
: 1;
// find the length of
// maximum length bitonic sequence
max = inc[0] + dec[0] - 1;
for (i = 1; i < n; i++)
if (inc[i] + dec[i] - 1 > max)
max = inc[i] + dec[i] - 1;
return max;
}
public void main(String[] args)
{
int[] arr = { 12, 4, 78, 90, 45, 23 };
System.out.println(bitonic(arr));
}
}
def bitonic(arr):
n = len(arr)
# length of increasing subarray
# ending at all indexes
inc = [0] * n
# length of decreasing subarray
# starting at all indexes
dec = [0] * n
# length of increasing sequence
# ending at first index is 1
inc[0] = 1
# length of increasing sequence
# starting at first index is 1
dec[n - 1] = 1
# construct increasing sequence array
for i in range(1, n):
inc[i] = inc[i - 1] + 1 if (arr[i] >= arr[i - 1]) else 1
# construct decreasing sequence array
for i in range(n - 2, -1, -1):
dec[i] = dec[i + 1] + 1 if (arr[i] >= arr[i + 1]) else 1
# find the length of
# maximum length bitonic sequence
max_val = inc[0] + dec[0] - 1
for i in range(1, n):
if (inc[i] + dec[i] - 1 > max_val):
max_val = inc[i] + dec[i] - 1
return max_val
if __name__ == "__main__":
arr = [12, 4, 78, 90, 45, 23]
print(bitonic(arr))
using System;
class Program {
public int bitonic(int[] arr)
{
int n = arr.Length;
// length of increasing subarray
// ending at all indexes
int[] inc = new int[n];
// length of decreasing subarray
// starting at all indexes
int[] dec = new int[n];
int i, max;
// length of increasing sequence
// ending at first index is 1
inc[0] = 1;
// length of increasing sequence
// starting at first index is 1
dec[n - 1] = 1;
// construct increasing sequence array
for (i = 1; i < n; i++)
inc[i] = (arr[i] >= arr[i - 1]) ? inc[i - 1] + 1
: 1;
// construct decreasing sequence array
for (i = n - 2; i >= 0; i--)
dec[i] = (arr[i] >= arr[i + 1]) ? dec[i + 1] + 1
: 1;
// find the length of
// maximum length bitonic sequence
max = inc[0] + dec[0] - 1;
for (i = 1; i < n; i++)
if (inc[i] + dec[i] - 1 > max)
max = inc[i] + dec[i] - 1;
return max;
}
void Main()
{
int[] arr = { 12, 4, 78, 90, 45, 23 };
Console.WriteLine(bitonic(arr));
}
}
function bitonic(arr)
{
let n = arr.length;
// length of increasing subarray
// ending at all indexes
let inc = new Array(n).fill(0);
// length of decreasing subarray
// starting at all indexes
let dec = new Array(n).fill(0);
let i, max;
// length of increasing sequence
// ending at first index is 1
inc[0] = 1;
// length of increasing sequence
// starting at first index is 1
dec[n - 1] = 1;
// construct increasing sequence array
for (i = 1; i < n; i++)
inc[i]
= (arr[i] >= arr[i - 1]) ? inc[i - 1] + 1 : 1;
// construct decreasing sequence array
for (i = n - 2; i >= 0; i--)
dec[i]
= (arr[i] >= arr[i + 1]) ? dec[i + 1] + 1 : 1;
// find the length of
// maximum length bitonic sequence
max = inc[0] + dec[0] - 1;
for (i = 1; i < n; i++)
if (inc[i] + dec[i] - 1 > max)
max = inc[i] + dec[i] - 1;
return max;
}
function main()
{
let arr = [ 12, 4, 78, 90, 45, 23 ];
console.log(bitonic(arr));
}
// Driver call
main();
Output
5
[Optimized Approach] Single Traversal - O(n) time and O(1) space
The idea is to find the longest bitonic subarray starting from each index arr[i]. From arr[i], the process first detects the end of the ascent (increasing sequence), then the descent (decreasing sequence). To handle overlapping subarrays, we record the nextStart position when two equal values are encountered during the descent phase. If the length of the current bitonic subarray exceeds the previously recorded maxLen, we update it.
Implementation:
- Initialize variables maxLen (defaults to 1), start (beginning of the current sequence), nextStart (potential start for the next sequence), and an iterator j = 0.
- Loop through the array as long as j < n-1.
- Using a while loop, keep incrementing
jas long as the sequence is non-decreasing (arr[j] <= arr[j+1]). This finds the peak. - Using another while loop, keep incrementing
jas long as the sequence is non-increasing (arr[j] >= arr[j+1]). - Inside the descent loop, whenever you encounter a strictly decreasing step (arr[j] > arr[j+1]), update nextStart = j+1. This safely marks where the next potential sequence should start.
- Calculate the length of the just-completed bitonic sequence (j - start +1) and update maxLen if it is strictly greater.
- Update
start = nextStart to prepare for the next iteration of the main loop. - Once the array is fully traversed, return maxLen.
Example:
arr[]:[12, 4, 78, 90, 45, 23] Initial State: maxLen = 1, start = 0, nextStart = 0, j= 0
- Iteration 1: start = 0 , j = 0→ Ascent skipped (12 > 4) → Descent advances
jto 1 (12 > 4) and sets nextStart = 1 → maxLen = max(1,1-0+1) =2 → start=1. - Iteration 2: start = 1, j = 1 → Ascent advances
jto 3 (4 < 78 < 90) → Descent advancesjto 5 (90 > 45 > 23), updating nextStart = 5 → maxLen = max(2,5-1+1) → start = 5. - Loop Terminates: Condition j < 5 is now false. Function returns
5.
#include <bits/stdc++.h>
using namespace std;
int bitonic(vector<int>& arr) {
int n = arr.size();
if (n == 0)
return 0;
int maxLen = 1;
int start = 0;
int nextStart = 0;
int j = 0;
while (j < n - 1) {
// look for the end of the ascent
while (j < n - 1 && arr[j] <= arr[j + 1])
j++;
// look for the end of the descent
while (j < n - 1 && arr[j] >= arr[j + 1]) {
// adjusting nextStart; this will be executed
// when we detect the start of the descent
if (j < n - 1 && arr[j] > arr[j + 1])
nextStart = j + 1;
j++;
}
maxLen = max(maxLen, j - start+1);
start = nextStart;
}
return maxLen;
}
int main() {
vector<int> arr = {12, 4, 78, 90, 45, 23};
cout << bitonic(arr) << endl;
return 0;
}
import java.util.Arrays;
import java.util.List;
public class GfG{
public int bitonic(int[] arr) {
int n = arr.length;
if (n == 0)
return 0;
int maxLen = 1;
int start = 0;
int nextStart = 0;
int j = 0;
while (j < n - 1) {
// look for the end of the ascent
while (j < n - 1 && arr[j] <= arr[j+1])
j++;
// look for the end of the descent
while (j < n - 1 && arr[j] >= arr[j+1]) {
// adjusting nextStart; this will be executed
//vwhen we detect the start of the descent
if (j < n - 1 && arr[j] > arr[j+1])
nextStart = j + 1;
j++;
}
maxLen = Math.max(maxLen, j - start+1);
start = nextStart;
}
return maxLen;
}
public void main(String[] args) {
int[] arr = {12, 4, 78, 90, 45, 23};
System.out.println(bitonic(arr));
}
}
def bitonic(arr):
n = len(arr)
if n == 0:
return 0
max_len = 1
start = 0
next_start = 0
j = 0
while j < n - 1:
# look for the end of the ascent
while j < n - 1 and arr[j] <= arr[j + 1]:
j += 1
# look for the end of the descent
while j < n - 1 and arr[j] >= arr[j + 1]:
# adjusting next_start; this will be
# executed when we detect the start of the descent
if j < n - 1 and arr[j] > arr[j + 1]:
next_start = j + 1
j += 1
max_len = max(max_len, j - start+1)
start = next_start
return max_len
if __name__ == "__main__":
arr = [12, 4, 78, 90, 45, 23]
print(bitonic(arr))
using System;
using System.Collections.Generic;
class GfG{
public int bitonic(int[] arr) {
int n = arr.Length;
if (n == 0)
return 0;
int maxLen = 1;
int start = 0;
int nextStart = 0;
int j = 0;
while (j < n - 1) {
// look for the end of the ascent
while (j < n - 1 && arr[j] <= arr[j + 1])
j++;
// look for the end of the descent
while (j < n - 1 && arr[j] >= arr[j + 1]) {
// adjusting nextStart; this will be
// executed when we detect the start of the descent
if (j < n - 1 && arr[j] > arr[j + 1])
nextStart = j + 1;
j++;
}
maxLen = Math.Max(maxLen, j - start+1);
start = nextStart;
}
return maxLen;
}
void Main() {
int[] arr = { 12, 4, 78, 90, 45, 23 };
Console.WriteLine(bitonic(arr));
}
}
function bitonic(arr) {
const n = arr.length;
if (n === 0)
return 0;
let maxLen = 1;
let start = 0;
let nextStart = 0;
let j = 0;
while (j < n - 1) {
// look for the end of the ascent
while (j < n - 1 && arr[j] <= arr[j + 1])
j++;
// look for the end of the descent
while (j < n - 1 && arr[j] >= arr[j + 1]) {
// adjusting nextStart; this will be
// executed when we detect the start of the descent
if (j < n - 1 && arr[j] > arr[j + 1])
nextStart = j + 1;
j++;
}
maxLen = Math.max(maxLen, j - start+1);
start = nextStart;
}
return maxLen;
}
const arr = [12, 4, 78, 90, 45, 23];
console.log(bitonic(arr));
Output
5