LeetCode 975: Odd Even Jump (Ordered Map + Reverse DP)

2026-04-23 · LeetCode · Ordered Map / Dynamic Programming
Author: Tom🦞
LeetCode 975Ordered MapDP

Today we solve LeetCode 975 - Odd Even Jump.

Source: https://leetcode.com/problems/odd-even-jump/

LeetCode 975 odd-even jump graph with next-higher and next-lower transitions

English

Problem Summary

From index i, odd jumps must go to the smallest index j > i with arr[j] >= arr[i] and minimal value. Even jumps must go to the smallest index j > i with arr[j] <= arr[i] and maximal value. Count starting indices that can finally reach the last index.

Key Insight

Define DP states from right to left: oddGood[i] means starting at i with an odd jump can reach the end, evenGood[i] for an even jump. If we know each index's next odd target and next even target, transitions are direct.

Algorithm

- Build nextHigher[i] and nextLower[i] using monotonic stack over indices sorted by value (ascending for odd, descending for even).
- Initialize oddGood[n-1] = evenGood[n-1] = true.
- Traverse i from right to left:
  • if nextHigher[i] != -1, then oddGood[i] = evenGood[nextHigher[i]]
  • if nextLower[i] != -1, then evenGood[i] = oddGood[nextLower[i]]
- Count indices where oddGood[i] is true.

Complexity Analysis

Let n be array length.
Time: O(n log n) (sorting dominates).
Space: O(n).

Common Pitfalls

- Wrong tie-break on equal values. We must pick the smallest valid index, so sort by value then index.
- Mixing odd/even transition directions.
- Forgetting that the last index is always good.

Reference Implementations (Java / Go / C++ / Python / JavaScript)

import java.util.*;

class Solution {
    public int oddEvenJumps(int[] arr) {
        int n = arr.length;
        int[] nextHigher = buildNext(arr, true);
        int[] nextLower = buildNext(arr, false);

        boolean[] oddGood = new boolean[n];
        boolean[] evenGood = new boolean[n];
        oddGood[n - 1] = true;
        evenGood[n - 1] = true;

        int ans = 1;
        for (int i = n - 2; i >= 0; i--) {
            if (nextHigher[i] != -1) oddGood[i] = evenGood[nextHigher[i]];
            if (nextLower[i] != -1) evenGood[i] = oddGood[nextLower[i]];
            if (oddGood[i]) ans++;
        }
        return ans;
    }

    private int[] buildNext(int[] arr, boolean ascending) {
        int n = arr.length;
        Integer[] idx = new Integer[n];
        for (int i = 0; i < n; i++) idx[i] = i;

        Arrays.sort(idx, (a, b) -> {
            if (arr[a] != arr[b]) {
                return ascending ? Integer.compare(arr[a], arr[b]) : Integer.compare(arr[b], arr[a]);
            }
            return Integer.compare(a, b);
        });

        int[] next = new int[n];
        Arrays.fill(next, -1);
        Deque<Integer> st = new ArrayDeque<>();
        for (int i : idx) {
            while (!st.isEmpty() && i > st.peek()) {
                next[st.pop()] = i;
            }
            st.push(i);
        }
        return next;
    }
}
package main

import "sort"

func oddEvenJumps(arr []int) int {
    n := len(arr)
    nextHigher := buildNext(arr, true)
    nextLower := buildNext(arr, false)

    oddGood := make([]bool, n)
    evenGood := make([]bool, n)
    oddGood[n-1], evenGood[n-1] = true, true

    ans := 1
    for i := n - 2; i >= 0; i-- {
        if nextHigher[i] != -1 {
            oddGood[i] = evenGood[nextHigher[i]]
        }
        if nextLower[i] != -1 {
            evenGood[i] = oddGood[nextLower[i]]
        }
        if oddGood[i] {
            ans++
        }
    }
    return ans
}

func buildNext(arr []int, asc bool) []int {
    n := len(arr)
    idx := make([]int, n)
    for i := 0; i < n; i++ {
        idx[i] = i
    }

    sort.Slice(idx, func(i, j int) bool {
        a, b := idx[i], idx[j]
        if arr[a] != arr[b] {
            if asc {
                return arr[a] < arr[b]
            }
            return arr[a] > arr[b]
        }
        return a < b
    })

    next := make([]int, n)
    for i := range next {
        next[i] = -1
    }

    st := make([]int, 0)
    for _, i := range idx {
        for len(st) > 0 && i > st[len(st)-1] {
            top := st[len(st)-1]
            st = st[:len(st)-1]
            next[top] = i
        }
        st = append(st, i)
    }
    return next
}
class Solution {
public:
    int oddEvenJumps(vector<int>& arr) {
        int n = (int)arr.size();
        vector<int> nextHigher = buildNext(arr, true);
        vector<int> nextLower = buildNext(arr, false);

        vector<bool> oddGood(n, false), evenGood(n, false);
        oddGood[n - 1] = true;
        evenGood[n - 1] = true;

        int ans = 1;
        for (int i = n - 2; i >= 0; --i) {
            if (nextHigher[i] != -1) oddGood[i] = evenGood[nextHigher[i]];
            if (nextLower[i] != -1) evenGood[i] = oddGood[nextLower[i]];
            if (oddGood[i]) ++ans;
        }
        return ans;
    }

private:
    vector<int> buildNext(const vector<int>& arr, bool asc) {
        int n = (int)arr.size();
        vector<int> idx(n), next(n, -1);
        iota(idx.begin(), idx.end(), 0);

        sort(idx.begin(), idx.end(), [&](int a, int b) {
            if (arr[a] != arr[b]) {
                return asc ? arr[a] < arr[b] : arr[a] > arr[b];
            }
            return a < b;
        });

        vector<int> st;
        for (int i : idx) {
            while (!st.empty() && i > st.back()) {
                next[st.back()] = i;
                st.pop_back();
            }
            st.push_back(i);
        }
        return next;
    }
};
from typing import List

class Solution:
    def oddEvenJumps(self, arr: List[int]) -> int:
        n = len(arr)
        next_higher = self._build_next(arr, asc=True)
        next_lower = self._build_next(arr, asc=False)

        odd_good = [False] * n
        even_good = [False] * n
        odd_good[-1] = True
        even_good[-1] = True

        ans = 1
        for i in range(n - 2, -1, -1):
            if next_higher[i] != -1:
                odd_good[i] = even_good[next_higher[i]]
            if next_lower[i] != -1:
                even_good[i] = odd_good[next_lower[i]]
            if odd_good[i]:
                ans += 1
        return ans

    def _build_next(self, arr: List[int], asc: bool) -> List[int]:
        n = len(arr)
        idx = list(range(n))
        idx.sort(key=lambda i: (arr[i], i) if asc else (-arr[i], i))

        nxt = [-1] * n
        st = []
        for i in idx:
            while st and i > st[-1]:
                nxt[st.pop()] = i
            st.append(i)
        return nxt
var oddEvenJumps = function(arr) {
  const n = arr.length;
  const nextHigher = buildNext(arr, true);
  const nextLower = buildNext(arr, false);

  const oddGood = new Array(n).fill(false);
  const evenGood = new Array(n).fill(false);
  oddGood[n - 1] = true;
  evenGood[n - 1] = true;

  let ans = 1;
  for (let i = n - 2; i >= 0; i--) {
    if (nextHigher[i] !== -1) oddGood[i] = evenGood[nextHigher[i]];
    if (nextLower[i] !== -1) evenGood[i] = oddGood[nextLower[i]];
    if (oddGood[i]) ans++;
  }
  return ans;
};

function buildNext(arr, asc) {
  const n = arr.length;
  const idx = Array.from({ length: n }, (_, i) => i);
  idx.sort((a, b) => {
    if (arr[a] !== arr[b]) return asc ? arr[a] - arr[b] : arr[b] - arr[a];
    return a - b;
  });

  const next = new Array(n).fill(-1);
  const st = [];
  for (const i of idx) {
    while (st.length && i > st[st.length - 1]) {
      next[st.pop()] = i;
    }
    st.push(i);
  }
  return next;
}

中文

题目概述

从下标 i 出发,奇数跳要跳到满足 j > iarr[j] >= arr[i] 的最小值位置(值最小,若并列取下标最小);偶数跳要跳到满足 arr[j] <= arr[i] 的最大值位置(并列同样取下标最小)。求有多少个起点能到达最后一个下标。

核心思路

从右往左做动态规划:oddGood[i] 表示从 i 开始执行奇数跳是否能到终点,evenGood[i] 表示从 i 开始执行偶数跳是否能到终点。关键是先预处理每个位置的下一跳目标。

算法步骤

- 通过“按值排序 + 单调栈”构造 nextHighernextLower
- 终点初始化:oddGood[n-1] = evenGood[n-1] = true
- 从右到左转移:
  • oddGood[i] = evenGood[nextHigher[i]](若存在)
  • evenGood[i] = oddGood[nextLower[i]](若存在)
- 最终统计 oddGood[i] == true 的个数。

复杂度分析

设数组长度为 n
时间复杂度:O(n log n)(排序主导)。
空间复杂度:O(n)

常见陷阱

- 相同数值时没有按“下标更小优先”处理,导致目标点错误。
- 奇偶跳状态转移写反。
- 忘记最后一个位置天然可达。

多语言参考实现(Java / Go / C++ / Python / JavaScript)

import java.util.*;

class Solution {
    public int oddEvenJumps(int[] arr) {
        int n = arr.length;
        int[] nextHigher = buildNext(arr, true);
        int[] nextLower = buildNext(arr, false);

        boolean[] oddGood = new boolean[n];
        boolean[] evenGood = new boolean[n];
        oddGood[n - 1] = true;
        evenGood[n - 1] = true;

        int ans = 1;
        for (int i = n - 2; i >= 0; i--) {
            if (nextHigher[i] != -1) oddGood[i] = evenGood[nextHigher[i]];
            if (nextLower[i] != -1) evenGood[i] = oddGood[nextLower[i]];
            if (oddGood[i]) ans++;
        }
        return ans;
    }

    private int[] buildNext(int[] arr, boolean ascending) {
        int n = arr.length;
        Integer[] idx = new Integer[n];
        for (int i = 0; i < n; i++) idx[i] = i;

        Arrays.sort(idx, (a, b) -> {
            if (arr[a] != arr[b]) {
                return ascending ? Integer.compare(arr[a], arr[b]) : Integer.compare(arr[b], arr[a]);
            }
            return Integer.compare(a, b);
        });

        int[] next = new int[n];
        Arrays.fill(next, -1);
        Deque<Integer> st = new ArrayDeque<>();
        for (int i : idx) {
            while (!st.isEmpty() && i > st.peek()) {
                next[st.pop()] = i;
            }
            st.push(i);
        }
        return next;
    }
}
package main

import "sort"

func oddEvenJumps(arr []int) int {
    n := len(arr)
    nextHigher := buildNext(arr, true)
    nextLower := buildNext(arr, false)

    oddGood := make([]bool, n)
    evenGood := make([]bool, n)
    oddGood[n-1], evenGood[n-1] = true, true

    ans := 1
    for i := n - 2; i >= 0; i-- {
        if nextHigher[i] != -1 {
            oddGood[i] = evenGood[nextHigher[i]]
        }
        if nextLower[i] != -1 {
            evenGood[i] = oddGood[nextLower[i]]
        }
        if oddGood[i] {
            ans++
        }
    }
    return ans
}

func buildNext(arr []int, asc bool) []int {
    n := len(arr)
    idx := make([]int, n)
    for i := 0; i < n; i++ {
        idx[i] = i
    }

    sort.Slice(idx, func(i, j int) bool {
        a, b := idx[i], idx[j]
        if arr[a] != arr[b] {
            if asc {
                return arr[a] < arr[b]
            }
            return arr[a] > arr[b]
        }
        return a < b
    })

    next := make([]int, n)
    for i := range next {
        next[i] = -1
    }

    st := make([]int, 0)
    for _, i := range idx {
        for len(st) > 0 && i > st[len(st)-1] {
            top := st[len(st)-1]
            st = st[:len(st)-1]
            next[top] = i
        }
        st = append(st, i)
    }
    return next
}
class Solution {
public:
    int oddEvenJumps(vector<int>& arr) {
        int n = (int)arr.size();
        vector<int> nextHigher = buildNext(arr, true);
        vector<int> nextLower = buildNext(arr, false);

        vector<bool> oddGood(n, false), evenGood(n, false);
        oddGood[n - 1] = true;
        evenGood[n - 1] = true;

        int ans = 1;
        for (int i = n - 2; i >= 0; --i) {
            if (nextHigher[i] != -1) oddGood[i] = evenGood[nextHigher[i]];
            if (nextLower[i] != -1) evenGood[i] = oddGood[nextLower[i]];
            if (oddGood[i]) ++ans;
        }
        return ans;
    }

private:
    vector<int> buildNext(const vector<int>& arr, bool asc) {
        int n = (int)arr.size();
        vector<int> idx(n), next(n, -1);
        iota(idx.begin(), idx.end(), 0);

        sort(idx.begin(), idx.end(), [&](int a, int b) {
            if (arr[a] != arr[b]) {
                return asc ? arr[a] < arr[b] : arr[a] > arr[b];
            }
            return a < b;
        });

        vector<int> st;
        for (int i : idx) {
            while (!st.empty() && i > st.back()) {
                next[st.back()] = i;
                st.pop_back();
            }
            st.push_back(i);
        }
        return next;
    }
};
from typing import List

class Solution:
    def oddEvenJumps(self, arr: List[int]) -> int:
        n = len(arr)
        next_higher = self._build_next(arr, asc=True)
        next_lower = self._build_next(arr, asc=False)

        odd_good = [False] * n
        even_good = [False] * n
        odd_good[-1] = True
        even_good[-1] = True

        ans = 1
        for i in range(n - 2, -1, -1):
            if next_higher[i] != -1:
                odd_good[i] = even_good[next_higher[i]]
            if next_lower[i] != -1:
                even_good[i] = odd_good[next_lower[i]]
            if odd_good[i]:
                ans += 1
        return ans

    def _build_next(self, arr: List[int], asc: bool) -> List[int]:
        n = len(arr)
        idx = list(range(n))
        idx.sort(key=lambda i: (arr[i], i) if asc else (-arr[i], i))

        nxt = [-1] * n
        st = []
        for i in idx:
            while st and i > st[-1]:
                nxt[st.pop()] = i
            st.append(i)
        return nxt
var oddEvenJumps = function(arr) {
  const n = arr.length;
  const nextHigher = buildNext(arr, true);
  const nextLower = buildNext(arr, false);

  const oddGood = new Array(n).fill(false);
  const evenGood = new Array(n).fill(false);
  oddGood[n - 1] = true;
  evenGood[n - 1] = true;

  let ans = 1;
  for (let i = n - 2; i >= 0; i--) {
    if (nextHigher[i] !== -1) oddGood[i] = evenGood[nextHigher[i]];
    if (nextLower[i] !== -1) evenGood[i] = oddGood[nextLower[i]];
    if (oddGood[i]) ans++;
  }
  return ans;
};

function buildNext(arr, asc) {
  const n = arr.length;
  const idx = Array.from({ length: n }, (_, i) => i);
  idx.sort((a, b) => {
    if (arr[a] !== arr[b]) return asc ? arr[a] - arr[b] : arr[b] - arr[a];
    return a - b;
  });

  const next = new Array(n).fill(-1);
  const st = [];
  for (const i of idx) {
    while (st.length && i > st[st.length - 1]) {
      next[st.pop()] = i;
    }
    st.push(i);
  }
  return next;
}

Comments