Given an array arr[], find the longest Zig-Zag subsequence problem such that all elements of this are alternating (arr[i-1] < arr[i] > arr[i+1] or arr[i-1] > arr[i] < arr[i+1]).
Examples :
Input: arr[] = [1, 5, 4]
Output: 3
Explanation: The entire sequence is a Zig-Zag sequence.Input: arr[] = [1, 17, 5, 10, 13, 15, 10, 5, 16, 8]
Output: 7
Explanation: There are several subsequences that achieve this length. One is [1, 17, 10, 13, 10, 16, 8].
Table of Content
[Naive Approach] Using Recursion with Previous Element - O(2^n) Time and O(n) Space
At each index, we either skip the current element or include it in the subsequence. We maintain the index of the previously selected (prev) and the expected direction (dir) of the next. Here,
- dir = 0 means the direction is not decided yet
- dir = 1 means the next selected element must be greater than the previous one
- dir = -1 means it must be smaller.
#include <iostream>
#include <vector>
using namespace std;
// Returns maximum Zig-Zag subsequence length starting from i
int maxSequence(int i, int prev, int dir, vector<int> &arr) {
if (i == arr.size())
return 0;
// Skip current element
int ans = maxSequence(i + 1, prev, dir, arr);
// Take first element of subsequence
if (prev == -1) {
ans = max(ans, 1 + maxSequence(i + 1, i, 0, arr));
}
// Direction not decided yet
else if (dir == 0) {
if (arr[i] > arr[prev]) {
ans = max(ans, 1 + maxSequence(i + 1, i, -1, arr));
}
if (arr[i] < arr[prev]) {
ans = max(ans, 1 + maxSequence(i + 1, i, 1, arr));
}
}
// Need a greater element
else if (dir == 1 && arr[i] > arr[prev]) {
ans = max(ans, 1 + maxSequence(i + 1, i, -1, arr));
}
// Need a smaller element
else if (dir == -1 && arr[i] < arr[prev]) {
ans = max(ans, 1 + maxSequence(i + 1, i, 1, arr));
}
return ans;
}
int longestZigZag(vector<int> &arr) {
return maxSequence(0, -1, 0, arr);
}
int main() {
vector<int> arr = {1, 5, 4};
cout << longestZigZag(arr);
return 0;
}
import java.util.Arrays;
public class GFG {
// Returns maximum Zig-Zag subsequence length starting from i
static int maxSequence(int i, int prev, int dir, int[] arr) {
if (i == arr.length)
return 0;
// Skip current element
int ans = maxSequence(i + 1, prev, dir, arr);
// Take first element of subsequence
if (prev == -1) {
ans = Math.max(ans, 1 + maxSequence(i + 1, i, 0, arr));
}
// Direction not decided yet
else if (dir == 0) {
if (arr[i] > arr[prev]) {
ans = Math.max(ans, 1 + maxSequence(i + 1, i, -1, arr));
}
if (arr[i] < arr[prev]) {
ans = Math.max(ans, 1 + maxSequence(i + 1, i, 1, arr));
}
}
// Need a greater element
else if (dir == 1 && arr[i] > arr[prev]) {
ans = Math.max(ans, 1 + maxSequence(i + 1, i, -1, arr));
}
// Need a smaller element
else if (dir == -1 && arr[i] < arr[prev]) {
ans = Math.max(ans, 1 + maxSequence(i + 1, i, 1, arr));
}
return ans;
}
static int longestZigZag(int[] arr) {
return maxSequence(0, -1, 0, arr);
}
public static void main(String[] args) {
int[] arr = {1, 5, 4};
System.out.println(longestZigZag(arr));
}
}
def maxSequence(i, prev, dir, arr):
if i == len(arr):
return 0
# Skip current element
ans = maxSequence(i + 1, prev, dir, arr)
# Take first element of subsequence
if prev == -1:
ans = max(ans, 1 + maxSequence(i + 1, i, 0, arr))
# Direction not decided yet
elif dir == 0:
if arr[i] > arr[prev]:
ans = max(ans, 1 + maxSequence(i + 1, i, -1, arr))
if arr[i] < arr[prev]:
ans = max(ans, 1 + maxSequence(i + 1, i, 1, arr))
# Need a greater element
elif dir == 1 and arr[i] > arr[prev]:
ans = max(ans, 1 + maxSequence(i + 1, i, -1, arr))
# Need a smaller element
elif dir == -1 and arr[i] < arr[prev]:
ans = max(ans, 1 + maxSequence(i + 1, i, 1, arr))
return ans
def longestZigZag(arr):
return maxSequence(0, -1, 0, arr)
if __name__ == '__main__':
arr = [1, 5, 4]
print(longestZigZag(arr))
using System;
class GFG
{
// Returns maximum Zig-Zag subsequence length starting from i
static int maxSequence(int i, int prev, int dir, int[] arr)
{
if (i == arr.Length)
return 0;
// Skip current element
int ans = maxSequence(i + 1, prev, dir, arr);
// Take first element of subsequence
if (prev == -1)
{
ans = Math.Max(ans, 1 + maxSequence(i + 1, i, 0, arr));
}
// Direction not decided yet
else if (dir == 0)
{
if (arr[i] > arr[prev])
{
ans = Math.Max(ans, 1 + maxSequence(i + 1, i, -1, arr));
}
if (arr[i] < arr[prev])
{
ans = Math.Max(ans, 1 + maxSequence(i + 1, i, 1, arr));
}
}
// Need a greater element
else if (dir == 1 && arr[i] > arr[prev])
{
ans = Math.Max(ans, 1 + maxSequence(i + 1, i, -1, arr));
}
// Need a smaller element
else if (dir == -1 && arr[i] < arr[prev])
{
ans = Math.Max(ans, 1 + maxSequence(i + 1, i, 1, arr));
}
return ans;
}
static int longestZigZag(int[] arr)
{
return maxSequence(0, -1, 0, arr);
}
static void Main()
{
int[] arr = { 1, 5, 4 };
Console.WriteLine(longestZigZag(arr));
}
}
function maxSequence(i, prev, dir, arr) {
if (i == arr.length)
return 0;
// Skip current element
let ans = maxSequence(i + 1, prev, dir, arr);
// Take first element of subsequence
if (prev == -1) {
ans = Math.max(ans, 1 + maxSequence(i + 1, i, 0, arr));
}
// Direction not decided yet
else if (dir == 0) {
if (arr[i] > arr[prev]) {
ans = Math.max(ans, 1 + maxSequence(i + 1, i, -1, arr));
}
if (arr[i] < arr[prev]) {
ans = Math.max(ans, 1 + maxSequence(i + 1, i, 1, arr));
}
}
// Need a greater element
else if (dir == 1 && arr[i] > arr[prev]) {
ans = Math.max(ans, 1 + maxSequence(i + 1, i, -1, arr));
}
// Need a smaller element
else if (dir == -1 && arr[i] < arr[prev]) {
ans = Math.max(ans, 1 + maxSequence(i + 1, i, 1, arr));
}
return ans;
}
function longestZigZag(arr) {
return maxSequence(0, -1, 0, arr);
}
// Driver code
const arr = [1, 5, 4];
console.log(longestZigZag(arr));
Output
3
[Better Approach] Dynamic Programming with Previous Elements - O(n^2) Time and O(n) Space
For every index i, we maintain two states:
- dp[i][0] stores the length of the longest Zig-Zag subsequence ending at i where the current element is greater than the previously selected.
- dp[i][1] stores the length where the current element is smaller than the previously selected
For every previous index j < i,
- If arr[j] < arr[i], arr[i] can be appended after a subsequence ending at j whose last element was smaller than its previous element, so we update dp[i][0].
- If arr[j] > arr[i], then arr[i] can be appended after a subsequence ending at j whose last element was greater than its previous element, so we update dp[i][1].
Recursive Formulation
dp[i][0] = max(dp[i][0], dp[j][1] + 1) , for all j < i and arr[j] < arr[i];
dp[i][1] = max(dp[i][1], dp[j][0] + 1), for all j < i and arr[j] > arr[i];
#include <iostream>
#include <vector>
using namespace std;
int longestZigZag(vector<int> &arr) {
int n = arr.size();
// dp[i][0] -> longest Zig-Zag subsequence ending at i
// where arr[i] is greater than the previous selected element,
// dp[i][1] -> longest Zig-Zag subsequence ending at i
// where arr[i] is smaller than the previous selected element
vector<vector<int>> dp(n, vector<int>(2, 1));
int ans = 1;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
// Append arr[i] after a subsequence ending at j
// whose last selected element was smaller
if (arr[j] < arr[i])
dp[i][0] = max(dp[i][0], dp[j][1] + 1);
// Append arr[i] after a subsequence ending at j
// whose last selected element was greater
else if (arr[j] > arr[i])
dp[i][1] = max(dp[i][1], dp[j][0] + 1);
}
ans = max({ans, dp[i][0], dp[i][1]});
}
return ans;
}
int main() {
vector<int> arr = {1, 5, 4};
cout << longestZigZag(arr);
return 0;
}
import java.util.Arrays;
public class GFG {
public static int longestZigZag(int[] arr) {
int n = arr.length;
// dp[i][0] -> longest Zig-Zag subsequence ending at i
// where arr[i] is greater than the previous selected element,
// dp[i][1] -> longest Zig-Zag subsequence ending at i
// where arr[i] is smaller than the previous selected element
int[][] dp = new int[n][2];
for (int[] row : dp) Arrays.fill(row, 1);
int ans = 1;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
// Append arr[i] after a subsequence ending at j
// whose last selected element was smaller
if (arr[j] < arr[i])
dp[i][0] = Math.max(dp[i][0], dp[j][1] + 1);
// Append arr[i] after a subsequence ending at j
// whose last selected element was greater
else if (arr[j] > arr[i])
dp[i][1] = Math.max(dp[i][1], dp[j][0] + 1);
}
ans = Math.max(ans, Math.max(dp[i][0], dp[i][1]));
}
return ans;
}
public static void main(String[] args) {
int[] arr = {1, 5, 4};
System.out.println(longestZigZag(arr));
}
}
def longestZigZag(arr):
n = len(arr)
# dp[i][0] -> longest Zig-Zag subsequence ending at i
# where arr[i] is greater than the previous selected element,
# dp[i][1] -> longest Zig-Zag subsequence ending at i
# where arr[i] is smaller than the previous selected element
dp = [[1, 1] for _ in range(n)]
ans = 1
for i in range(1, n):
for j in range(i):
# Append arr[i] after a subsequence ending at j
# whose last selected element was smaller
if arr[j] < arr[i]:
dp[i][0] = max(dp[i][0], dp[j][1] + 1)
# Append arr[i] after a subsequence ending at j
# whose last selected element was greater
elif arr[j] > arr[i]:
dp[i][1] = max(dp[i][1], dp[j][0] + 1)
ans = max(ans, dp[i][0], dp[i][1])
return ans
if __name__ == '__main__':
arr = [1, 5, 4]
print(longestZigZag(arr))
using System;
using System.Linq;
public class GFG
{
public static int longestZigZag(int[] arr)
{
int n = arr.Length;
// dp[i][0] -> longest Zig-Zag subsequence ending at i
// where arr[i] is greater than the previous selected element,
// dp[i][1] -> longest Zig-Zag subsequence ending at i
// where arr[i] is smaller than the previous selected element
int[,] dp = new int[n, 2];
for (int i = 0; i < n; i++)
{
dp[i, 0] = 1;
dp[i, 1] = 1;
}
int ans = 1;
for (int i = 1; i < n; i++)
{
for (int j = 0; j < i; j++)
{
// Append arr[i] after a subsequence ending at j
// whose last selected element was smaller
if (arr[j] < arr[i])
dp[i, 0] = Math.Max(dp[i, 0], dp[j, 1] + 1);
// Append arr[i] after a subsequence ending at j
// whose last selected element was greater
else if (arr[j] > arr[i])
dp[i, 1] = Math.Max(dp[i, 1], dp[j, 0] + 1);
}
ans = Math.Max(ans, Math.Max(dp[i, 0], dp[i, 1]));
}
return ans;
}
public static void Main()
{
int[] arr = { 1, 5, 4 };
Console.WriteLine(longestZigZag(arr));
}
}
function longestZigZag(arr) {
const n = arr.length;
// dp[i][0] -> longest Zig-Zag subsequence ending at i
// where arr[i] is greater than the previous selected element,
// dp[i][1] -> longest Zig-Zag subsequence ending at i
// where arr[i] is smaller than the previous selected element
const dp = Array.from({ length: n }, () => Array(2).fill(1));
let ans = 1;
for (let i = 1; i < n; i++) {
for (let j = 0; j < i; j++) {
// Append arr[i] after a subsequence ending at j
// whose last selected element was smaller
if (arr[j] < arr[i])
dp[i][0] = Math.max(dp[i][0], dp[j][1] + 1);
// Append arr[i] after a subsequence ending at j
// whose last selected element was greater
else if (arr[j] > arr[i])
dp[i][1] = Math.max(dp[i][1], dp[j][0] + 1);
}
ans = Math.max(ans, Math.max(dp[i][0], dp[i][1]));
}
return ans;
}
// Driver code
const arr = [1, 5, 4];
console.log(longestZigZag(arr));
Output
3
[Expected Approach] Dynamic Programming with State Variables - O(n) Time and O(1) Space
While traversing the array, we compare each element with its previous element. If the current comparison (greater or smaller) differs from the last valid comparison, then the current element can extend the Zig-Zag subsequence. Equal elements are ignored since they neither increase nor decrease the sequence. This allows us to find the answer in a single traversal.
- Initialize
up = 1anddown = 1(single element is both up and down sequence). - Traverse array from index 1 to n-1.
- If arr
[i] > arr[i-1], updateup = down + 1. - If arr
[i] < arr[i-1], updatedown = up + 1. - If equal, no update needed.
- Return
max(up, down).
#include <iostream>
#include <vector>
using namespace std;
int longestZigZag(vector<int>& arr) {
int n = arr.size();
if (n == 0)
return 0;
// Length of longest Zig-Zag subsequence
// ending with an upward movement
int up = 1;
// Length of longest Zig-Zag subsequence
// ending with a downward movement
int down = 1;
for (int i = 1; i < n; i++) {
// Current element is greater than previous,
// so we can extend a sequence that previously
// ended with a downward movement
if (arr[i] > arr[i - 1]) {
up = down + 1;
}
// Current element is smaller than previous,
// so we can extend a sequence that previously
// ended with an upward movement
else if (arr[i] < arr[i - 1]) {
down = up + 1;
}
// Equal elements do not help in forming
// a Zig-Zag pattern, so ignore them
}
return max(up, down);
}
int main() {
vector<int> arr = {1, 5, 4};
cout << longestZigZag(arr);
return 0;
}
class GFG {
static int longestZigZag(int[] arr) {
int n = arr.length;
if (n == 0)
return 0;
// Length of longest Zig-Zag subsequence
// ending with an upward movement
int up = 1;
// Length of longest Zig-Zag subsequence
// ending with a downward movement
int down = 1;
for (int i = 1; i < n; i++) {
// Current element is greater than previous,
// so we can extend a sequence that previously
// ended with a downward movement
if (arr[i] > arr[i - 1]) {
up = down + 1;
}
// Current element is smaller than previous,
// so we can extend a sequence that previously
// ended with an upward movement
else if (arr[i] < arr[i - 1]) {
down = up + 1;
}
// Equal elements do not help in forming
// a Zig-Zag pattern, so ignore them
}
return Math.max(up, down);
}
public static void main(String[] args) {
int[] arr = {1, 5, 4};
System.out.println(longestZigZag(arr));
}
}
def longestZigZag(arr):
n = len(arr)
if n == 0:
return 0
# Length of longest Zig-Zag subsequence
# ending with an upward movement
up = 1
# Length of longest Zig-Zag subsequence
# ending with a downward movement
down = 1
for i in range(1, n):
# Current element is greater than previous,
# so we can extend a sequence that previously
# ended with a downward movement
if arr[i] > arr[i - 1]:
up = down + 1
# Current element is smaller than previous,
# so we can extend a sequence that previously
# ended with an upward movement
elif arr[i] < arr[i - 1]:
down = up + 1
# Equal elements do not help in forming
# a Zig-Zag pattern, so ignore them
return max(up, down)
if __name__ == "__main__":
arr = [1, 5, 4]
print(longestZigZag(arr))
using System;
class GFG {
static int longestZigZag(int[] arr) {
int n = arr.Length;
if (n == 0)
return 0;
// Length of longest Zig-Zag subsequence
// ending with an upward movement
int up = 1;
// Length of longest Zig-Zag subsequence
// ending with a downward movement
int down = 1;
for (int i = 1; i < n; i++) {
// Current element is greater than previous,
// so we can extend a sequence that previously
// ended with a downward movement
if (arr[i] > arr[i - 1]) {
up = down + 1;
}
// Current element is smaller than previous,
// so we can extend a sequence that previously
// ended with an upward movement
else if (arr[i] < arr[i - 1]) {
down = up + 1;
}
// Equal elements do not help in forming
// a Zig-Zag pattern, so ignore them
}
return Math.Max(up, down);
}
static void Main(string[] args) {
int[] arr = {1, 5, 4};
Console.WriteLine(longestZigZag(arr));
}
}
function longestZigZag(arr) {
const n = arr.length;
if (n === 0)
return 0;
// Length of longest Zig-Zag subsequence
// ending with an upward movement
let up = 1;
// Length of longest Zig-Zag subsequence
// ending with a downward movement
let down = 1;
for (let i = 1; i < n; i++) {
// Current element is greater than previous,
// so we can extend a sequence that previously
// ended with a downward movement
if (arr[i] > arr[i - 1]) {
up = down + 1;
}
// Current element is smaller than previous,
// so we can extend a sequence that previously
// ended with an upward movement
else if (arr[i] < arr[i - 1]) {
down = up + 1;
}
// Equal elements do not help in forming
// a Zig-Zag pattern, so ignore them
}
return Math.max(up, down);
}
// Driver code
const arr = [1, 5, 4];
console.log(longestZigZag(arr));
Output
3