Longest Zig-Zag Subsequence

Last Updated : 14 Jul, 2026

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].

Try It Yourself
redirect icon

[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.
C++
#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;
}
Java
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));
    }
}
Python
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))
C#
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));
    }
}
JavaScript
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];

C++
#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;
}
Java
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));
    }
}
Python
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))
C#
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));
    }
}
JavaScript
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 = 1 and down = 1 (single element is both up and down sequence).
  • Traverse array from index 1 to n-1.
  • If arr[i] > arr[i-1], update up = down + 1.
  • If arr[i] < arr[i-1], update down = up + 1.
  • If equal, no update needed.
  • Return max(up, down).
C++
#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;
}
Java
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));
    }
}
Python
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))
C#
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));
    }
}
JavaScript
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
Comment