普通视图

发现新文章,点击刷新页面。
今天 — 2026年2月11日首页

最长平衡子数组 II

2026年2月5日 10:43

前置知识

该方法假定读者已经熟练掌握前缀和与线段树的相关知识与应用。

方法一:前缀和 + 线段树

本题的关键突破口是将题意中的“奇数元素种类”和“偶数元素种类”以一种量化的方式转换为数据结构可以处理的问题,具体而言,我们可以设出现一种奇数元素记为 $-1$,出现一种偶数元素记为 $1$,子数组平衡的条件即为转换后所有元素之和为 $0$。

这样转换后,易观察出我们其实得到了一个差分数组,只不过将奇数元素记为 $-1$,偶元素记为 $1$。因此对其计算前缀和,前缀和为 $0$ 时说明对应前缀子数组是平衡的。因此在固定左边界的情况下,最长的平衡子数组的右边界即为该前缀和中最后一个 $0$ 所在的位置。

由于该差分数组的变化量绝对值不超过 $1$,因此前缀和满足离散条件下的介值定理,可以使用线段树寻找最右边的 $0$,具体计算方式如下:

  1. 同时维护区间最大值和最小值。
  2. 判断 $0$ 是否存在于右区间(位于最大值最小值构成的区间内),若存在则只搜索右区间。
  3. 否则,搜索左区间。

由于满足离散条件下的介值定理,故可以直接通过最大值和最小值判断目标值 $0$ 是否在待搜索区间内,因此也能在 $O(\log n)$ 的时间内搜索完毕。

接下来的思路就是遍历左端点,寻找前缀和对应的最右侧的 $0$ 所在位置,得到最长平衡子数组的长度。设当前左边界下标是 $i$,当前最长平衡子数组长度是 $l$,有一个小优化是搜索的起点可以从 $i + l$ 开始,因为更近的结果即便找到也不能更新答案。

最后一个问题是向右移动左端点的过程中,如何撤销前一个位置的元素对前缀和的贡献。

先让我们从差分与前缀和的定义开始理解:差分数组中某位置 $i$ 的非零值 $v_i$,会累加到该位置及其之后的所有前缀和中。例如,若位置 $1$ 的差分贡献为 $-1$,则它会让 $S_1, S_2, \dots, S_N$ 的值都减小 $1$;再比如,若元素 $x$ 先后出现在位置 $p_1$ 和 $p_2$,我们可以认为位置 $p_1$ 处的 $x$ 负责区间 $[p_1, p_2 - 1]$ 上的贡献,而位置 $p_2$ 处的 $x$ 则负责 $[p_2, \dots]$ 上的贡献。

$$
[ \dots, 0, \underbrace{1, 1, \dots, 1}{\text{由第 1 个 x 贡献}}, \underbrace{1, 1, \dots, 1}{\text{由第 2 个 x 贡献}}, \dots ]
$$

因此,我们可以将每种元素出现的所有位置记录到各自的队列中,在更新左边界时,得到要撤销贡献的元素在前缀和中的贡献区间,然后在该区间上减去它的贡献即可。显然,这样区间加法操作也可以使用线段树完成。

基于以上算法,我们先统计前缀和以及元素出现的次数,然后不断更新左端点,使用线段树维护前缀和,寻找最右侧的 $0$,并更新全局最优解即可。

代码

###C++

struct LazyTag {
    int to_add = 0;

    LazyTag& operator+=(const LazyTag& other) {
        this->to_add += other.to_add;
        return *this;
    }

    bool has_tag() const { return to_add != 0; }

    void clear() { to_add = 0; }
};

struct SegmentTreeNode {
    int min_value = 0;
    int max_value = 0;
    // int data = 0; // 只有叶子节点使用, 本题不需要
    LazyTag lazy_tag;
};

class SegmentTree {
public:
    int n;
    vector<SegmentTreeNode> tree;

    SegmentTree(const vector<int>& data) : n(data.size()) {
        tree.resize(n * 4 + 1);
        build(data, 1, n, 1);
    }

    void add(int l, int r, int val) {
        LazyTag tag{val};
        update(l, r, tag, 1, n, 1);
    }

    int find_last(int start, int val) {
        if (start > n) {
            return -1;
        }
        return find(start, n, val, 1, n, 1);
    }

private:
    inline void apply_tag(int i, const LazyTag& tag) {
        tree[i].min_value += tag.to_add;
        tree[i].max_value += tag.to_add;
        tree[i].lazy_tag += tag;
    }

    inline void pushdown(int i) {
        if (tree[i].lazy_tag.has_tag()) {
            LazyTag tag = tree[i].lazy_tag;
            apply_tag(i << 1, tag);
            apply_tag(i << 1 | 1, tag);
            tree[i].lazy_tag.clear();
        }
    }

    inline void pushup(int i) {
        tree[i].min_value =
            std::min(tree[i << 1].min_value, tree[i << 1 | 1].min_value);
        tree[i].max_value =
            std::max(tree[i << 1].max_value, tree[i << 1 | 1].max_value);
    }

    void build(const vector<int>& data, int l, int r, int i) {
        if (l == r) {
            tree[i].min_value = tree[i].max_value = data[l - 1];
            return;
        }

        int mid = l + ((r - l) >> 1);
        build(data, l, mid, i << 1);
        build(data, mid + 1, r, i << 1 | 1);

        pushup(i);
    }

    void update(int target_l, int target_r, const LazyTag& tag, int l, int r,
                int i) {
        if (target_l <= l && r <= target_r) {
            apply_tag(i, tag);
            return;
        }

        pushdown(i);
        int mid = l + ((r - l) >> 1);
        if (target_l <= mid)
            update(target_l, target_r, tag, l, mid, i << 1);
        if (target_r > mid)
            update(target_l, target_r, tag, mid + 1, r, i << 1 | 1);
        pushup(i);
    }

    int find(int target_l, int target_r, int val, int l, int r, int i) {
        if (tree[i].min_value > val || tree[i].max_value < val) {
            return -1;
        }

        // 根据介值定理,此时区间内必然存在解
        if (l == r) {
            return l;
        }

        pushdown(i);
        int mid = l + ((r - l) >> 1);

        // target_l 一定小于等于 r(=n)
        if (target_r >= mid + 1) {
            int res = find(target_l, target_r, val, mid + 1, r, i << 1 | 1);
            if (res != -1)
                return res;
        }

        if (l <= target_r && mid >= target_l) {
            return find(target_l, target_r, val, l, mid, i << 1);
        }

        return -1;
    }
};

class Solution {
public:
    int longestBalanced(vector<int>& nums) {
        map<int, queue<int>> occurrences;
        auto sgn = [](int x) { return (x % 2) == 0 ? 1 : -1; };

        int len = 0;
        vector<int> prefix_sum(nums.size(), 0);

        prefix_sum[0] = sgn(nums[0]);
        occurrences[nums[0]].push(1);

        for (int i = 1; i < nums.size(); i++) {
            prefix_sum[i] = prefix_sum[i - 1];
            auto& occ = occurrences[nums[i]];
            if (occ.empty()) {
                prefix_sum[i] += sgn(nums[i]);
            }
            occ.push(i + 1);
        }

        SegmentTree seg(prefix_sum);

        for (int i = 0; i < nums.size(); i++) {
            len = std::max(len, seg.find_last(i + len, 0) - i);

            auto next_pos = nums.size() + 1;
            occurrences[nums[i]].pop();
            if (!occurrences[nums[i]].empty()) {
                next_pos = occurrences[nums[i]].front();
            }

            seg.add(i + 1, next_pos - 1, -sgn(nums[i]));
        }

        return len;
    }
};

###JavaScript

class LazyTag {
    constructor() {
        this.toAdd = 0;
    }

    add(other) {
        this.toAdd += other.toAdd;
        return this;
    }

    hasTag() {
        return this.toAdd !== 0;
    }

    clear() {
        this.toAdd = 0;
    }
}

class SegmentTreeNode {
    constructor() {
        this.minValue = 0;
        this.maxValue = 0;
        // int data = 0; // 只有叶子节点使用, 本题不需要
        this.lazyTag = new LazyTag();
    }
}

class SegmentTree {
    constructor(data) {
        this.n = data.length;
        this.tree = new Array(this.n * 4 + 1).fill(null).map(() => new SegmentTreeNode());
        this.build(data, 1, this.n, 1);
    }

    add(l, r, val) {
        const tag = new LazyTag();
        tag.toAdd = val;
        this.update(l, r, tag, 1, this.n, 1);
    }

    findLast(start, val) {
        if (start > this.n) {
            return -1;
        }
        return this.find(start, this.n, val, 1, this.n, 1);
    }

    applyTag(i, tag) {
        this.tree[i].minValue += tag.toAdd;
        this.tree[i].maxValue += tag.toAdd;
        this.tree[i].lazyTag.add(tag);
    }

    pushdown(i) {
        if (this.tree[i].lazyTag.hasTag()) {
            const tag = new LazyTag();
            tag.toAdd = this.tree[i].lazyTag.toAdd;
            this.applyTag(i << 1, tag);
            this.applyTag((i << 1) | 1, tag);
            this.tree[i].lazyTag.clear();
        }
    }

    pushup(i) {
        this.tree[i].minValue = Math.min(this.tree[i << 1].minValue, this.tree[(i << 1) | 1].minValue);
        this.tree[i].maxValue = Math.max(this.tree[i << 1].maxValue, this.tree[(i << 1) | 1].maxValue);
    }

    build(data, l, r, i) {
        if (l == r) {
            this.tree[i].minValue = this.tree[i].maxValue = data[l - 1];
            return;
        }

        const mid = l + ((r - l) >> 1);
        this.build(data, l, mid, i << 1);
        this.build(data, mid + 1, r, (i << 1) | 1);

        this.pushup(i);
    }

    update(targetL, targetR, tag, l, r, i) {
        if (targetL <= l && r <= targetR) {
            this.applyTag(i, tag);
            return;
        }

        this.pushdown(i);
        const mid = l + ((r - l) >> 1);
        if (targetL <= mid)
            this.update(targetL, targetR, tag, l, mid, i << 1);
        if (targetR > mid)
            this.update(targetL, targetR, tag, mid + 1, r, (i << 1) | 1);
        this.pushup(i);
    }

    find(targetL, targetR, val, l, r, i) {
        if (this.tree[i].minValue > val || this.tree[i].maxValue < val) {
            return -1;
        }

        // 根据介值定理,此时区间内必然存在解
        if (l == r) {
            return l;
        }

        this.pushdown(i);
        const mid = l + ((r - l) >> 1);
        // targetL 一定小于等于 r(=n)
        if (targetR >= mid + 1) {
            const res = this.find(targetL, targetR, val, mid + 1, r, (i << 1) | 1);
            if (res != -1)
                return res;
        }

        if (l <= targetR && mid >= targetL) {
            return this.find(targetL, targetR, val, l, mid, i << 1);
        }

        return -1;
    }
}

var longestBalanced = function(nums) {
    const occurrences = new Map();
    const sgn = (x) => (x % 2 == 0 ? 1 : -1);

    let len = 0;
    const prefixSum = new Array(nums.length).fill(0);

    prefixSum[0] = sgn(nums[0]);
    if (!occurrences.has(nums[0])) occurrences.set(nums[0], new Queue());
    occurrences.get(nums[0]).push(1);

    for (let i = 1; i < nums.length; i++) {
        prefixSum[i] = prefixSum[i - 1];
        if (!occurrences.has(nums[i]))
            occurrences.set(nums[i], new Queue());
        const occ = occurrences.get(nums[i]);
        if (occ.size() === 0) {
            prefixSum[i] += sgn(nums[i]);
        }
        occ.push(i + 1);
    }

    const seg = new SegmentTree(prefixSum);

    for (let i = 0; i < nums.length; i++) {
        len = Math.max(len, seg.findLast(i + len, 0) - i);

        let nextPos = nums.length + 1;
        const occ = occurrences.get(nums[i]);
        occ.pop();
        if (occ.size() > 0) {
            nextPos = occ.front();
        }

        seg.add(i + 1, nextPos - 1, -sgn(nums[i]));
    }

    return len;
}

###TypeScript

class LazyTag {
    toAdd: number = 0;

    add(other: LazyTag): LazyTag {
        this.toAdd += other.toAdd;
        return this;
    }

    hasTag(): boolean {
        return this.toAdd !== 0;
    }

    clear(): void {
        this.toAdd = 0;
    }
}

class SegmentTreeNode {
    minValue: number = 0;
    maxValue: number = 0;
    // int data = 0; // 只有叶子节点使用, 本题不需要
    lazyTag: LazyTag = new LazyTag();
}

class SegmentTree {
    n: number;
    tree: SegmentTreeNode[];

    constructor(data: number[]) {
        this.n = data.length;
        this.tree = new Array(this.n * 4 + 1).fill(null).map(() => new SegmentTreeNode());
        this.build(data, 1, this.n, 1);
    }

    add(l: number, r: number, val: number): void {
        const tag = new LazyTag();
        tag.toAdd = val;
        this.update(l, r, tag, 1, this.n, 1);
    }

    findLast(start: number, val: number): number {
        if (start > this.n) {
            return -1;
        }
        return this.find(start, this.n, val, 1, this.n, 1);
    }

    private applyTag(i: number, tag: LazyTag): void {
        this.tree[i].minValue += tag.toAdd;
        this.tree[i].maxValue += tag.toAdd;
        this.tree[i].lazyTag.add(tag);
    }

    private pushdown(i: number): void {
        if (this.tree[i].lazyTag.hasTag()) {
            const tag = new LazyTag();
            tag.toAdd = this.tree[i].lazyTag.toAdd;
            this.applyTag(i << 1, tag);
            this.applyTag((i << 1) | 1, tag);
            this.tree[i].lazyTag.clear();
        }
    }

    private pushup(i: number): void {
        this.tree[i].minValue = Math.min(
            this.tree[i << 1].minValue,
            this.tree[(i << 1) | 1].minValue,
        );
        this.tree[i].maxValue = Math.max(
            this.tree[i << 1].maxValue,
            this.tree[(i << 1) | 1].maxValue,
        );
    }

    private build(data: number[], l: number, r: number, i: number): void {
        if (l == r) {
            this.tree[i].minValue = this.tree[i].maxValue = data[l - 1];
            return;
        }

        const mid = l + ((r - l) >> 1);
        this.build(data, l, mid, i << 1);
        this.build(data, mid + 1, r, (i << 1) | 1);

        this.pushup(i);
    }
    private update(
        targetL: number,
        targetR: number,
        tag: LazyTag,
        l: number,
        r: number,
        i: number,
    ): void {
        if (targetL <= l && r <= targetR) {
            this.applyTag(i, tag);
            return;
        }

        this.pushdown(i);
        const mid = l + ((r - l) >> 1);
        if (targetL <= mid) this.update(targetL, targetR, tag, l, mid, i << 1);
        if (targetR > mid) this.update(targetL, targetR, tag, mid + 1, r, (i << 1) | 1);
        this.pushup(i);
    }

    private find(
        targetL: number,
        targetR: number,
        val: number,
        l: number,
        r: number,
        i: number,
    ): number {
        if (this.tree[i].minValue > val || this.tree[i].maxValue < val) {
            return -1;
        }

        // 根据介值定理,此时区间内必然存在解
        if (l == r) {
            return l;
        }

        this.pushdown(i);
        const mid = l + ((r - l) >> 1);

        // targetL 一定小于等于 r(=n)
        if (targetR >= mid + 1) {
            const res = this.find(targetL, targetR, val, mid + 1, r, (i << 1) | 1);
            if (res != -1) return res;
        }

        if (l <= targetR && mid >= targetL) {
            return this.find(targetL, targetR, val, l, mid, i << 1);
        }

        return -1;
    }
}

function longestBalanced(nums: number[]): number {
    const occurrences = new Map<number, Queue<number>>();
    const sgn = (x: number) => (x % 2 == 0 ? 1 : -1);

    let len = 0;
    const prefixSum: number[] = new Array(nums.length).fill(0);

    prefixSum[0] = sgn(nums[0]);
    if (!occurrences.has(nums[0])) occurrences.set(nums[0], new Queue());
    occurrences.get(nums[0])!.push(1);

    for (let i = 1; i < nums.length; i++) {
        prefixSum[i] = prefixSum[i - 1];
        if (!occurrences.has(nums[i])) occurrences.set(nums[i], new Queue());
        const occ = occurrences.get(nums[i])!;
        if (occ.size() === 0) {
            prefixSum[i] += sgn(nums[i]);
        }
        occ.push(i + 1);
    }

    const seg = new SegmentTree(prefixSum);

    for (let i = 0; i < nums.length; i++) {
        len = Math.max(len, seg.findLast(i + len, 0) - i);

        let nextPos = nums.length + 1;
        const occ = occurrences.get(nums[i])!;
        occ.pop();
        if (occ.size() > 0) {
            nextPos = occ.front();
        }

        seg.add(i + 1, nextPos - 1, -sgn(nums[i]));
    }

    return len;
}

###Java

class LazyTag {
    int toAdd;
    
    LazyTag() {
        this.toAdd = 0;
    }
    
    LazyTag add(LazyTag other) {
        this.toAdd += other.toAdd;
        return this;
    }
    
    boolean hasTag() {
        return this.toAdd != 0;
    }
    
    void clear() {
        this.toAdd = 0;
    }
}

class SegmentTreeNode {
    int minValue;
    int maxValue;
    LazyTag lazyTag;
    
    SegmentTreeNode() {
        this.minValue = 0;
        this.maxValue = 0;
        this.lazyTag = new LazyTag();
    }
}

class SegmentTree {
    private int n;
    private SegmentTreeNode[] tree;
    
    SegmentTree(int[] data) {
        this.n = data.length;
        this.tree = new SegmentTreeNode[this.n * 4 + 1];
        for (int i = 0; i < tree.length; i++) {
            tree[i] = new SegmentTreeNode();
        }
        build(data, 1, this.n, 1);
    }
    
    void add(int l, int r, int val) {
        LazyTag tag = new LazyTag();
        tag.toAdd = val;
        update(l, r, tag, 1, this.n, 1);
    }
    
    int findLast(int start, int val) {
        if (start > this.n) {
            return -1;
        }
        return find(start, this.n, val, 1, this.n, 1);
    }
    
    private void applyTag(int i, LazyTag tag) {
        tree[i].minValue += tag.toAdd;
        tree[i].maxValue += tag.toAdd;
        tree[i].lazyTag.add(tag);
    }
    
    private void pushdown(int i) {
        if (tree[i].lazyTag.hasTag()) {
            LazyTag tag = new LazyTag();
            tag.toAdd = tree[i].lazyTag.toAdd;
            applyTag(i << 1, tag);
            applyTag((i << 1) | 1, tag);
            tree[i].lazyTag.clear();
        }
    }
    
    private void pushup(int i) {
        tree[i].minValue = Math.min(tree[i << 1].minValue, tree[(i << 1) | 1].minValue);
        tree[i].maxValue = Math.max(tree[i << 1].maxValue, tree[(i << 1) | 1].maxValue);
    }
    
    private void build(int[] data, int l, int r, int i) {
        if (l == r) {
            tree[i].minValue = tree[i].maxValue = data[l - 1];
            return;
        }
        
        int mid = l + ((r - l) >> 1);
        build(data, l, mid, i << 1);
        build(data, mid + 1, r, (i << 1) | 1);
        pushup(i);
    }
    
    private void update(int targetL, int targetR, LazyTag tag, int l, int r, int i) {
        if (targetL <= l && r <= targetR) {
            applyTag(i, tag);
            return;
        }
        
        pushdown(i);
        int mid = l + ((r - l) >> 1);
        if (targetL <= mid)
            update(targetL, targetR, tag, l, mid, i << 1);
        if (targetR > mid)
            update(targetL, targetR, tag, mid + 1, r, (i << 1) | 1);
        pushup(i);
    }
    
    private int find(int targetL, int targetR, int val, int l, int r, int i) {
        if (tree[i].minValue > val || tree[i].maxValue < val) {
            return -1;
        }
        
        if (l == r) {
            return l;
        }
        
        pushdown(i);
        int mid = l + ((r - l) >> 1);
        
        if (targetR >= mid + 1) {
            int res = find(targetL, targetR, val, mid + 1, r, (i << 1) | 1);
            if (res != -1)
                return res;
        }
        
        if (l <= targetR && mid >= targetL) {
            return find(targetL, targetR, val, l, mid, i << 1);
        }
        
        return -1;
    }
}

class Solution {
    public int longestBalanced(int[] nums) {
        Map<Integer, Queue<Integer>> occurrences = new HashMap<>();
        
        int len = 0;
        int[] prefixSum = new int[nums.length];
        prefixSum[0] = sgn(nums[0]);
        occurrences.computeIfAbsent(nums[0], k -> new LinkedList<>()).add(1);
        
        for (int i = 1; i < nums.length; i++) {
            prefixSum[i] = prefixSum[i - 1];
            Queue<Integer> occ = occurrences.computeIfAbsent(nums[i], k -> new LinkedList<>());
            if (occ.isEmpty()) {
                prefixSum[i] += sgn(nums[i]);
            }
            occ.add(i + 1);
        }
        
        SegmentTree seg = new SegmentTree(prefixSum);
        
        for (int i = 0; i < nums.length; i++) {
            len = Math.max(len, seg.findLast(i + len, 0) - i);
            
            int nextPos = nums.length + 1;
            occurrences.get(nums[i]).poll();
            if (!occurrences.get(nums[i]).isEmpty()) {
                nextPos = occurrences.get(nums[i]).peek();
            }
            
            seg.add(i + 1, nextPos - 1, -sgn(nums[i]));
        }
        
        return len;
    }
    
    private int sgn(int x) {
        return (x % 2) == 0 ? 1 : -1;
    }
}

###C#

public class LazyTag {
    public int toAdd;
    
    public LazyTag() {
        this.toAdd = 0;
    }
    
    public LazyTag Add(LazyTag other) {
        this.toAdd += other.toAdd;
        return this;
    }
    
    public bool HasTag() {
        return this.toAdd != 0;
    }
    
    public void Clear() {
        this.toAdd = 0;
    }
}

public class SegmentTreeNode {
    public int minValue;
    public int maxValue;
    public LazyTag lazyTag;
    
    public SegmentTreeNode() {
        this.minValue = 0;
        this.maxValue = 0;
        this.lazyTag = new LazyTag();
    }
}

public class SegmentTree {
    private int n;
    private SegmentTreeNode[] tree;
    
    public SegmentTree(int[] data) {
        this.n = data.Length;
        this.tree = new SegmentTreeNode[this.n * 4 + 1];
        for (int i = 0; i < tree.Length; i++) {
            tree[i] = new SegmentTreeNode();
        }
        Build(data, 1, this.n, 1);
    }
    
    public void Add(int l, int r, int val) {
        LazyTag tag = new LazyTag();
        tag.toAdd = val;
        Update(l, r, tag, 1, this.n, 1);
    }
    
    public int FindLast(int start, int val) {
        if (start > this.n) {
            return -1;
        }
        return Find(start, this.n, val, 1, this.n, 1);
    }
    
    private void ApplyTag(int i, LazyTag tag) {
        tree[i].minValue += tag.toAdd;
        tree[i].maxValue += tag.toAdd;
        tree[i].lazyTag.Add(tag);
    }
    
    private void Pushdown(int i) {
        if (tree[i].lazyTag.HasTag()) {
            LazyTag tag = new LazyTag();
            tag.toAdd = tree[i].lazyTag.toAdd;
            ApplyTag(i << 1, tag);
            ApplyTag((i << 1) | 1, tag);
            tree[i].lazyTag.Clear();
        }
    }
    
    private void Pushup(int i) {
        tree[i].minValue = Math.Min(tree[i << 1].minValue, tree[(i << 1) | 1].minValue);
        tree[i].maxValue = Math.Max(tree[i << 1].maxValue, tree[(i << 1) | 1].maxValue);
    }
    
    private void Build(int[] data, int l, int r, int i) {
        if (l == r) {
            tree[i].minValue = tree[i].maxValue = data[l - 1];
            return;
        }
        
        int mid = l + ((r - l) >> 1);
        Build(data, l, mid, i << 1);
        Build(data, mid + 1, r, (i << 1) | 1);
        Pushup(i);
    }
    
    private void Update(int targetL, int targetR, LazyTag tag, int l, int r, int i) {
        if (targetL <= l && r <= targetR) {
            ApplyTag(i, tag);
            return;
        }
        
        Pushdown(i);
        int mid = l + ((r - l) >> 1);
        if (targetL <= mid)
            Update(targetL, targetR, tag, l, mid, i << 1);
        if (targetR > mid)
            Update(targetL, targetR, tag, mid + 1, r, (i << 1) | 1);
        Pushup(i);
    }
    
    private int Find(int targetL, int targetR, int val, int l, int r, int i) {
        if (tree[i].minValue > val || tree[i].maxValue < val) {
            return -1;
        }
        
        if (l == r) {
            return l;
        }
        
        Pushdown(i);
        int mid = l + ((r - l) >> 1);
        
        if (targetR >= mid + 1) {
            int res = Find(targetL, targetR, val, mid + 1, r, (i << 1) | 1);
            if (res != -1)
                return res;
        }
        
        if (l <= targetR && mid >= targetL) {
            return Find(targetL, targetR, val, l, mid, i << 1);
        }
        
        return -1;
    }
}

public class Solution {
    public int LongestBalanced(int[] nums) {
        var occurrences = new Dictionary<int, Queue<int>>();
        
        int len = 0;
        int[] prefixSum = new int[nums.Length];
        prefixSum[0] = Sgn(nums[0]);
        if (!occurrences.ContainsKey(nums[0])) {
            occurrences[nums[0]] = new Queue<int>();
        }
        occurrences[nums[0]].Enqueue(1);
        
        for (int i = 1; i < nums.Length; i++) {
            prefixSum[i] = prefixSum[i - 1];
            if (!occurrences.ContainsKey(nums[i])) {
                occurrences[nums[i]] = new Queue<int>();
            }
            var occ = occurrences[nums[i]];
            if (occ.Count == 0) {
                prefixSum[i] += Sgn(nums[i]);
            }
            occ.Enqueue(i + 1);
        }
        
        var seg = new SegmentTree(prefixSum);
        for (int i = 0; i < nums.Length; i++) {
            len = Math.Max(len, seg.FindLast(i + len, 0) - i);
            
            int nextPos = nums.Length + 1;
            occurrences[nums[i]].Dequeue();
            if (occurrences[nums[i]].Count > 0) {
                nextPos = occurrences[nums[i]].Peek();
            }
            
            seg.Add(i + 1, nextPos - 1, -Sgn(nums[i]));
        }
        
        return len;
    }
    
    private int Sgn(int x) {
        return (x % 2) == 0 ? 1 : -1;
    }
}

###Python

class LazyTag:
    def __init__(self):
        self.to_add = 0

    def add(self, other):
        self.to_add += other.to_add
        return self

    def has_tag(self):
        return self.to_add != 0

    def clear(self):
        self.to_add = 0

class SegmentTreeNode:
    def __init__(self):
        self.min_value = 0
        self.max_value = 0
        self.lazy_tag = LazyTag()

class SegmentTree:
    def __init__(self, data):
        self.n = len(data)
        self.tree = [SegmentTreeNode() for _ in range(self.n * 4 + 1)]
        self._build(data, 1, self.n, 1)

    def add(self, l, r, val):
        tag = LazyTag()
        tag.to_add = val
        self._update(l, r, tag, 1, self.n, 1)

    def find_last(self, start, val):
        if start > self.n:
            return -1
        return self._find(start, self.n, val, 1, self.n, 1)

    def _apply_tag(self, i, tag):
        self.tree[i].min_value += tag.to_add
        self.tree[i].max_value += tag.to_add
        self.tree[i].lazy_tag.add(tag)

    def _pushdown(self, i):
        if self.tree[i].lazy_tag.has_tag():
            tag = LazyTag()
            tag.to_add = self.tree[i].lazy_tag.to_add
            self._apply_tag(i << 1, tag)
            self._apply_tag((i << 1) | 1, tag)
            self.tree[i].lazy_tag.clear()

    def _pushup(self, i):
        self.tree[i].min_value = min(self.tree[i << 1].min_value,
                                     self.tree[(i << 1) | 1].min_value)
        self.tree[i].max_value = max(self.tree[i << 1].max_value,
                                     self.tree[(i << 1) | 1].max_value)

    def _build(self, data, l, r, i):
        if l == r:
            self.tree[i].min_value = data[l - 1]
            self.tree[i].max_value = data[l - 1]
            return

        mid = l + ((r - l) >> 1)
        self._build(data, l, mid, i << 1)
        self._build(data, mid + 1, r, (i << 1) | 1)
        self._pushup(i)

    def _update(self, target_l, target_r, tag, l, r, i):
        if target_l <= l and r <= target_r:
            self._apply_tag(i, tag)
            return

        self._pushdown(i)
        mid = l + ((r - l) >> 1)
        if target_l <= mid:
            self._update(target_l, target_r, tag, l, mid, i << 1)
        if target_r > mid:
            self._update(target_l, target_r, tag, mid + 1, r, (i << 1) | 1)
        self._pushup(i)

    def _find(self, target_l, target_r, val, l, r, i):
        if self.tree[i].min_value > val or self.tree[i].max_value < val:
            return -1

        if l == r:
            return l

        self._pushdown(i)
        mid = l + ((r - l) >> 1)

        if target_r >= mid + 1:
            res = self._find(target_l, target_r, val, mid + 1, r, (i << 1) | 1)
            if res != -1:
                return res

        if l <= target_r and mid >= target_l:
            return self._find(target_l, target_r, val, l, mid, i << 1)

        return -1

class Solution:
    def longestBalanced(self, nums: List[int]) -> int:
        occurrences = defaultdict(deque)
        
        def sgn(x):
            return 1 if x % 2 == 0 else -1
        
        length = 0
        prefix_sum = [0] * len(nums)
        prefix_sum[0] = sgn(nums[0])
        occurrences[nums[0]].append(1)
        
        for i in range(1, len(nums)):
            prefix_sum[i] = prefix_sum[i - 1]
            occ = occurrences[nums[i]]
            if not occ:
                prefix_sum[i] += sgn(nums[i])
            occ.append(i + 1)
        
        seg = SegmentTree(prefix_sum)
        for i in range(len(nums)):
            length = max(length, seg.find_last(i + length, 0) - i)
            next_pos = len(nums) + 1
            occurrences[nums[i]].popleft()
            if occurrences[nums[i]]:
                next_pos = occurrences[nums[i]][0]
            
            seg.add(i + 1, next_pos - 1, -sgn(nums[i]))
        
        return length

###Go

type LazyTag struct {
    toAdd int
}

func (l *LazyTag) Add(other *LazyTag) *LazyTag {
    l.toAdd += other.toAdd
    return l
}

func (l *LazyTag) HasTag() bool {
    return l.toAdd != 0
}

func (l *LazyTag) Clear() {
    l.toAdd = 0
}

type SegmentTreeNode struct {
    minValue int
    maxValue int
    lazyTag  *LazyTag
}

func NewSegmentTreeNode() *SegmentTreeNode {
    return &SegmentTreeNode{
        minValue: 0,
        maxValue: 0,
        lazyTag:  &LazyTag{},
    }
}

type SegmentTree struct {
    n    int
    tree []*SegmentTreeNode
}

func NewSegmentTree(data []int) *SegmentTree {
    n := len(data)
    tree := make([]*SegmentTreeNode, n*4+1)
    for i := range tree {
        tree[i] = NewSegmentTreeNode()
    }
    seg := &SegmentTree{n: n, tree: tree}
    seg.build(data, 1, n, 1)
    return seg
}

func (seg *SegmentTree) Add(l, r, val int) {
    tag := &LazyTag{toAdd: val}
    seg.update(l, r, tag, 1, seg.n, 1)
}

func (seg *SegmentTree) FindLast(start, val int) int {
    if start > seg.n {
        return -1
    }
    return seg.find(start, seg.n, val, 1, seg.n, 1)
}

func (seg *SegmentTree) applyTag(i int, tag *LazyTag) {
    seg.tree[i].minValue += tag.toAdd
    seg.tree[i].maxValue += tag.toAdd
    seg.tree[i].lazyTag.Add(tag)
}

func (seg *SegmentTree) pushdown(i int) {
    if seg.tree[i].lazyTag.HasTag() {
        tag := &LazyTag{toAdd: seg.tree[i].lazyTag.toAdd}
        seg.applyTag(i<<1, tag)
        seg.applyTag((i<<1)|1, tag)
        seg.tree[i].lazyTag.Clear()
    }
}

func (seg *SegmentTree) pushup(i int) {
    left := seg.tree[i<<1]
    right := seg.tree[(i<<1)|1]
    seg.tree[i].minValue = min(left.minValue, right.minValue)
    seg.tree[i].maxValue = max(left.maxValue, right.maxValue)
}

func (seg *SegmentTree) build(data []int, l, r, i int) {
    if l == r {
        seg.tree[i].minValue = data[l-1]
        seg.tree[i].maxValue = data[l-1]
        return
    }

    mid := l + ((r - l) >> 1)
    seg.build(data, l, mid, i<<1)
    seg.build(data, mid+1, r, (i<<1)|1)
    seg.pushup(i)
}

func (seg *SegmentTree) update(targetL, targetR int, tag *LazyTag, l, r, i int) {
    if targetL <= l && r <= targetR {
        seg.applyTag(i, tag)
        return
    }

    seg.pushdown(i)
    mid := l + ((r - l) >> 1)
    if targetL <= mid {
        seg.update(targetL, targetR, tag, l, mid, i<<1)
    }
    if targetR > mid {
        seg.update(targetL, targetR, tag, mid+1, r, (i<<1)|1)
    }
    seg.pushup(i)
}

func (seg *SegmentTree) find(targetL, targetR, val, l, r, i int) int {
    if seg.tree[i].minValue > val || seg.tree[i].maxValue < val {
        return -1
    }

    if l == r {
        return l
    }

    seg.pushdown(i)
    mid := l + ((r - l) >> 1)

    if targetR >= mid+1 {
        res := seg.find(targetL, targetR, val, mid+1, r, (i<<1)|1)
        if res != -1 {
            return res
        }
    }

    if l <= targetR && mid >= targetL {
        return seg.find(targetL, targetR, val, l, mid, i<<1)
    }

    return -1
}

func longestBalanced(nums []int) int {
    occurrences := make(map[int][]int)
    
    sgn := func(x int) int {
        if x%2 == 0 {
            return 1
        }
        return -1
    }
    
    length := 0
    prefixSum := make([]int, len(nums))
    prefixSum[0] = sgn(nums[0])
    occurrences[nums[0]] = append(occurrences[nums[0]], 1)
    
    for i := 1; i < len(nums); i++ {
        prefixSum[i] = prefixSum[i-1]
        occ := occurrences[nums[i]]
        if len(occ) == 0 {
            prefixSum[i] += sgn(nums[i])
        }
        occurrences[nums[i]] = append(occ, i+1)
    }
    
    seg := NewSegmentTree(prefixSum)
    for i := 0; i < len(nums); i++ {
        length = max(length, seg.FindLast(i+length, 0)-i)
        nextPos := len(nums) + 1
        occurrences[nums[i]] = occurrences[nums[i]][1:]
        if len(occurrences[nums[i]]) > 0 {
            nextPos = occurrences[nums[i]][0]
        }
        
        seg.Add(i+1, nextPos-1, -sgn(nums[i]))
    }
    
    return length
}

###C

typedef struct ListNode ListNode;

typedef struct {
    ListNode *head;
    int size;
} List;

typedef struct {
    int key;
    List *val;
    UT_hash_handle hh;
} HashItem;

List* listCreate() {
    List *list = (List*)malloc(sizeof(List));
    list->head = NULL;
    list->size = 0;
    return list;
}

void listPush(List *list, int val) {
    ListNode *node = (ListNode*)malloc(sizeof(ListNode));
    node->val = val;
    node->next = list->head;
    list->head = node;
    list->size++;
}

void listPop(List *list) {
    if (list->head == NULL) return;
    ListNode *temp = list->head;
    list->head = list->head->next;
    free(temp);
    list->size--;
}

int listAt(List *list, int index) {
    ListNode *cur = list->head;
    for (int i = 0; i < index && cur != NULL; i++) {
        cur = cur->next;
    }
    return cur ? cur->val : -1;
}

void listReverse(List *list) {
    ListNode *prev = NULL;
    ListNode *cur = list->head;
    ListNode *next = NULL;
    while (cur != NULL) {
        next = cur->next;
        cur->next = prev;
        prev = cur;
        cur = next;
    }
    list->head = prev;
}

void listFree(List *list) {
    while (list->head != NULL) {
        listPop(list);
    }
    free(list);
}

HashItem* hashFindItem(HashItem **obj, int key) {
    HashItem *pEntry = NULL;
    HASH_FIND_INT(*obj, &key, pEntry);
    return pEntry;
}

bool hashAddItem(HashItem **obj, int key, List *val) {
    if (hashFindItem(obj, key)) {
        return false;
    }
    HashItem *pEntry = (HashItem*)malloc(sizeof(HashItem));
    pEntry->key = key;
    pEntry->val = val;
    HASH_ADD_INT(*obj, key, pEntry);
    return true;
}

List* hashGetItem(HashItem **obj, int key) {
    HashItem *pEntry = hashFindItem(obj, key);
    if (!pEntry) {
        List *newList = listCreate();
        hashAddItem(obj, key, newList);
        return newList;
    }
    return pEntry->val;
}

void hashFree(HashItem **obj) {
    HashItem *curr = NULL, *tmp = NULL;
    HASH_ITER(hh, *obj, curr, tmp) {
        HASH_DEL(*obj, curr);
        listFree(curr->val);
        free(curr);
    }
}

void hashIterate(HashItem **obj, void (*callback)(HashItem *item)) {
    HashItem *curr = NULL, *tmp = NULL;
    HASH_ITER(hh, *obj, curr, tmp) {
        callback(curr);
    }
}

typedef struct {
    int toAdd;
} LazyTag;

void lazyTagAdd(LazyTag *tag, LazyTag *other) {
    tag->toAdd += other->toAdd;
}

bool lazyTagHasTag(LazyTag *tag) {
    return tag->toAdd != 0;
}

void lazyTagClear(LazyTag *tag) {
    tag->toAdd = 0;
}

typedef struct {
    int minValue;
    int maxValue;
    LazyTag lazyTag;
} SegmentTreeNode;

typedef struct {
    int n;
    SegmentTreeNode *tree;
} SegmentTree;

void segmentTreeApplyTag(SegmentTree *seg, int i, LazyTag *tag) {
    seg->tree[i].minValue += tag->toAdd;
    seg->tree[i].maxValue += tag->toAdd;
    lazyTagAdd(&seg->tree[i].lazyTag, tag);
}

void segmentTreePushdown(SegmentTree *seg, int i) {
    if (lazyTagHasTag(&seg->tree[i].lazyTag)) {
        LazyTag tag = {seg->tree[i].lazyTag.toAdd};
        segmentTreeApplyTag(seg, i << 1, &tag);
        segmentTreeApplyTag(seg, (i << 1) | 1, &tag);
        lazyTagClear(&seg->tree[i].lazyTag);
    }
}

void segmentTreePushup(SegmentTree *seg, int i) {
    seg->tree[i].minValue = fmin(seg->tree[i << 1].minValue, seg->tree[(i << 1) | 1].minValue);
    seg->tree[i].maxValue = fmax(seg->tree[i << 1].maxValue, seg->tree[(i << 1) | 1].maxValue);
}

void segmentTreeBuild(SegmentTree *seg, int *data, int l, int r, int i) {
    if (l == r) {
        seg->tree[i].minValue = seg->tree[i].maxValue = data[l - 1];
        return;
    }

    int mid = l + ((r - l) >> 1);
    segmentTreeBuild(seg, data, l, mid, i << 1);
    segmentTreeBuild(seg, data, mid + 1, r, (i << 1) | 1);
    segmentTreePushup(seg, i);
}

void segmentTreeUpdate(SegmentTree *seg, int targetL, int targetR, LazyTag *tag,
                       int l, int r, int i) {
    if (targetL <= l && r <= targetR) {
        segmentTreeApplyTag(seg, i, tag);
        return;
    }

    segmentTreePushdown(seg, i);
    int mid = l + ((r - l) >> 1);
    if (targetL <= mid) {
        segmentTreeUpdate(seg, targetL, targetR, tag, l, mid, i << 1);
    }
    if (targetR > mid) {
        segmentTreeUpdate(seg, targetL, targetR, tag, mid + 1, r, (i << 1) | 1);
    }
    segmentTreePushup(seg, i);
}

int segmentTreeFind(SegmentTree *seg, int targetL, int targetR, int val,
                    int l, int r, int i) {
    if (seg->tree[i].minValue > val || seg->tree[i].maxValue < val) {
        return -1;
    }
    if (l == r) {
        return l;
    }

    segmentTreePushdown(seg, i);
    int mid = l + ((r - l) >> 1);
    if (targetR >= mid + 1) {
        int res = segmentTreeFind(seg, targetL, targetR, val, mid + 1, r, (i << 1) | 1);
        if (res != -1) {
            return res;
        }
    }
    if (targetL <= mid) {
        return segmentTreeFind(seg, targetL, targetR, val, l, mid, i << 1);
    }

    return -1;
}

SegmentTree* segmentTreeCreate(int *data, int n) {
    SegmentTree *seg = (SegmentTree*)malloc(sizeof(SegmentTree));
    seg->n = n;
    seg->tree = (SegmentTreeNode*)calloc(n * 4 + 1, sizeof(SegmentTreeNode));
    segmentTreeBuild(seg, data, 1, n, 1);
    return seg;
}

void segmentTreeAdd(SegmentTree *seg, int l, int r, int val) {
    LazyTag tag = {val};
    segmentTreeUpdate(seg, l, r, &tag, 1, seg->n, 1);
}

int segmentTreeFindLast(SegmentTree *seg, int start, int val) {
    if (start > seg->n) {
        return -1;
    }
    return segmentTreeFind(seg, start, seg->n, val, 1, seg->n, 1);
}

void segmentTreeFree(SegmentTree *seg) {
    free(seg->tree);
    free(seg);
}

int sgn(int x) {
    return (x % 2 == 0) ? 1 : -1;
}

void reverseList(HashItem *item) {
    listReverse(item->val);
}

int longestBalanced(int* nums, int numsSize) {
    HashItem *occurrences = NULL;
    int len = 0;
    int *prefixSum = (int*)calloc(numsSize, sizeof(int));

    prefixSum[0] = sgn(nums[0]);
    List *list0 = hashGetItem(&occurrences, nums[0]);
    listPush(list0, 1);
    for (int i = 1; i < numsSize; i++) {
        prefixSum[i] = prefixSum[i - 1];
        List *occ = hashGetItem(&occurrences, nums[i]);
        if (occ->size == 0) {
            prefixSum[i] += sgn(nums[i]);
        }
        listPush(occ, i + 1);
    }

    hashIterate(&occurrences, reverseList);
    SegmentTree *seg = segmentTreeCreate(prefixSum, numsSize);
    for (int i = 0; i < numsSize; i++) {
        int findResult = segmentTreeFindLast(seg, i + len, 0);
        int newLen = findResult - i;
        if (newLen > len) {
            len = newLen;
        }

        int nextPos = numsSize + 1;
        List *occ = hashGetItem(&occurrences, nums[i]);
        listPop(occ);
        if (occ->size > 0) {
            nextPos = listAt(occ, 0);
        }
        segmentTreeAdd(seg, i + 1, nextPos - 1, -sgn(nums[i]));
    }

    segmentTreeFree(seg);
    free(prefixSum);
    hashFree(&occurrences);

    return len;
}

###Rust

use std::collections::{HashMap, VecDeque};
use std::cmp::max;

#[derive(Debug, Clone, Copy)]
struct LazyTag {
    add: i32,
}

impl LazyTag {
    fn new() -> Self {
        LazyTag { add: 0 }
    }
    
    fn is_empty(&self) -> bool {
        self.add == 0
    }
    
    fn combine(&mut self, other: &LazyTag) {
        self.add += other.add;
    }
    
    fn clear(&mut self) {
        self.add = 0;
    }
}

#[derive(Debug, Clone)]
struct Node {
    min_val: i32,
    max_val: i32,
    lazy: LazyTag,
}

impl Node {
    fn new() -> Self {
        Node {
            min_val: 0,
            max_val: 0,
            lazy: LazyTag::new(),
        }
    }
}

struct SegmentTree {
    n: usize,
    tree: Vec<Node>,
}

impl SegmentTree {
    fn new(data: &[i32]) -> Self {
        let n = data.len();
        let mut tree = vec![Node::new(); 4 * n];
        let mut seg = SegmentTree { n, tree };
        seg.build(data, 1, n, 1);
        seg
    }
    
    fn build(&mut self, data: &[i32], l: usize, r: usize, idx: usize) {
        if l == r {
            self.tree[idx].min_val = data[l - 1];
            self.tree[idx].max_val = data[l - 1];
            return;
        }
        
        let mid = (l + r) / 2;
        self.build(data, l, mid, idx * 2);
        self.build(data, mid + 1, r, idx * 2 + 1);
        self.push_up(idx);
    }
    
    fn push_up(&mut self, idx: usize) {
        let left_min = self.tree[idx * 2].min_val;
        let left_max = self.tree[idx * 2].max_val;
        let right_min = self.tree[idx * 2 + 1].min_val;
        let right_max = self.tree[idx * 2 + 1].max_val;
        
        self.tree[idx].min_val = left_min.min(right_min);
        self.tree[idx].max_val = left_max.max(right_max);
    }
    
    fn apply(&mut self, idx: usize, tag: &LazyTag) {
        self.tree[idx].min_val += tag.add;
        self.tree[idx].max_val += tag.add;
        self.tree[idx].lazy.combine(tag);
    }
    
    fn push_down(&mut self, idx: usize) {
        if self.tree[idx].lazy.is_empty() {
            return;
        }
        
        let tag = self.tree[idx].lazy;
        self.apply(idx * 2, &tag);
        self.apply(idx * 2 + 1, &tag);
        self.tree[idx].lazy.clear();
    }
    
    fn range_add(&mut self, l: usize, r: usize, val: i32) {
        if l > r || l > self.n || r < 1 {
            return;
        }
        let tag = LazyTag { add: val };
        self._update(l, r, &tag, 1, self.n, 1);
    }
    
    fn _update(&mut self, ql: usize, qr: usize, tag: &LazyTag, 
              l: usize, r: usize, idx: usize) {
        if ql > r || qr < l {
            return;
        }
        
        if ql <= l && r <= qr {
            self.apply(idx, tag);
            return;
        }
        
        self.push_down(idx);
        let mid = (l + r) / 2;
        if ql <= mid {
            self._update(ql, qr, tag, l, mid, idx * 2);
        }
        if qr > mid {
            self._update(ql, qr, tag, mid + 1, r, idx * 2 + 1);
        }
        self.push_up(idx);
    }
    
    fn find_last_zero(&mut self, start: usize, val: i32) -> i32 {
        if start > self.n {
            return -1;
        }
        self._find(start, self.n, val, 1, self.n, 1)
    }
    
    fn _find(&mut self, ql: usize, qr: usize, val: i32, 
            l: usize, r: usize, idx: usize) -> i32 {
        if l > qr || r < ql || self.tree[idx].min_val > val || self.tree[idx].max_val < val {
            return -1;
        }
        
        if l == r {
            return l as i32;
        }
        
        self.push_down(idx);
        let mid = (l + r) / 2;
        let right_res = self._find(ql, qr, val, mid + 1, r, idx * 2 + 1);
        if right_res != -1 {
            return right_res;
        }
        
        self._find(ql, qr, val, l, mid, idx * 2)
    }
    
    fn query_min(&self, l: usize, r: usize) -> i32 {
        self._query_min(l, r, 1, self.n, 1)
    }
    
    fn _query_min(&self, ql: usize, qr: usize, l: usize, r: usize, idx: usize) -> i32 {
        if ql > r || qr < l {
            return i32::MAX;
        }
        
        if ql <= l && r <= qr {
            return self.tree[idx].min_val;
        }
        
        let mid = (l + r) / 2;
        let left_min = self._query_min(ql, qr, l, mid, idx * 2);
        let right_min = self._query_min(ql, qr, mid + 1, r, idx * 2 + 1);
        left_min.min(right_min)
    }
}

impl Solution {
    pub fn longest_balanced(nums: Vec<i32>) -> i32 {
        let n = nums.len();
        if n == 0 {
            return 0;
        }
        
        fn sign(x: i32) -> i32 {
            if x % 2 == 0 { 1 } else { -1 }
        }
        
        let mut prefix_sum = vec![0; n];
        prefix_sum[0] = sign(nums[0]);
        let mut pos_map: HashMap<i32, VecDeque<usize>> = HashMap::new();
        pos_map.entry(nums[0]).or_insert_with(VecDeque::new).push_back(1);
        
        for i in 1..n {
            prefix_sum[i] = prefix_sum[i - 1];
            let positions = pos_map.entry(nums[i]).or_insert_with(VecDeque::new);
            if positions.is_empty() {
                prefix_sum[i] += sign(nums[i]);
            }
            positions.push_back(i + 1);
        }
        
        let mut seg_tree = SegmentTree::new(&prefix_sum);
        let mut max_len = 0;
        
        for i in 0..n {
            let start_idx = i + max_len as usize;
            if start_idx < n {
                let last_pos = seg_tree.find_last_zero(start_idx + 1, 0);
                if last_pos != -1 {
                    max_len = max(max_len, last_pos - i as i32);
                }
            }
            
            let num = nums[i];
            let next_pos = pos_map.get_mut(&num)
                .and_then(|positions| {
                    positions.pop_front();
                    positions.front().copied()
                })
                .unwrap_or(n + 2);
            
            let delta = -sign(num);
            if i + 1 <= next_pos - 1 {
                seg_tree.range_add(i + 1, next_pos - 1, delta);
            }
        }
        
        max_len
    }
}

复杂度分析

  • 时间复杂度:$O(n \log n)$,其中 $n$ 是 $\textit{nums}$ 的长度。预处理元素出现下标以及前缀和需要 $O(n \log n)$,线段树建树需要 $O(n \log n)$,后续遍历寻找合法区间需要 $O(n)$,循环内读取映射集需要 $O(\log n)$,使用线段树进行上界查找和区间加都需要 $O(\log n)$,故主循环需要 $O(n \log n)$。最后总时间复杂度为 $O(n \log n)$。

  • 空间复杂度:$O(n)$。线段树需要 $O(n)$ 的空间,队列和映射集总计需要 $O(n)$ 的空间。

昨天以前首页

平衡二叉树

2020年8月16日 23:00

前言

这道题中的平衡二叉树的定义是:二叉树的每个节点的左右子树的高度差的绝对值不超过 $1$,则二叉树是平衡二叉树。根据定义,一棵二叉树是平衡二叉树,当且仅当其所有子树也都是平衡二叉树,因此可以使用递归的方式判断二叉树是不是平衡二叉树,递归的顺序可以是自顶向下或者自底向上。

方法一:自顶向下的递归

定义函数 $\texttt{height}$,用于计算二叉树中的任意一个节点 $p$ 的高度:

$$
\texttt{height}(p) =
\begin{cases}
0 & p \text{ 是空节点}\
\max(\texttt{height}(p.\textit{left}), \texttt{height}(p.\textit{right}))+1 & p \text{ 是非空节点}
\end{cases}
$$

有了计算节点高度的函数,即可判断二叉树是否平衡。具体做法类似于二叉树的前序遍历,即对于当前遍历到的节点,首先计算左右子树的高度,如果左右子树的高度差是否不超过 $1$,再分别递归地遍历左右子节点,并判断左子树和右子树是否平衡。这是一个自顶向下的递归的过程。

<fig1,fig2,fig3,fig4,fig5,fig6,fig7,fig8,fig9,fig10,fig11,fig12,fig13,fig14,fig15,fig16,fig17,fig18,fig19,fig20,fig21,fig22,fig23,fig24,fig25,fig26,fig27,fig28,fig29,fig30,fig31>

###Java

class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null) {
            return true;
        } else {
            return Math.abs(height(root.left) - height(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
        }
    }

    public int height(TreeNode root) {
        if (root == null) {
            return 0;
        } else {
            return Math.max(height(root.left), height(root.right)) + 1;
        }
    }
}

###C++

class Solution {
public:
    int height(TreeNode* root) {
        if (root == NULL) {
            return 0;
        } else {
            return max(height(root->left), height(root->right)) + 1;
        }
    }

    bool isBalanced(TreeNode* root) {
        if (root == NULL) {
            return true;
        } else {
            return abs(height(root->left) - height(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
        }
    }
};

###Python

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def height(root: TreeNode) -> int:
            if not root:
                return 0
            return max(height(root.left), height(root.right)) + 1

        if not root:
            return True
        return abs(height(root.left) - height(root.right)) <= 1 and self.isBalanced(root.left) and self.isBalanced(root.right)

###C

int height(struct TreeNode* root) {
    if (root == NULL) {
        return 0;
    } else {
        return fmax(height(root->left), height(root->right)) + 1;
    }
}

bool isBalanced(struct TreeNode* root) {
    if (root == NULL) {
        return true;
    } else {
        return fabs(height(root->left) - height(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
    }
}

###golang

func isBalanced(root *TreeNode) bool {
    if root == nil {
        return true
    }
    return abs(height(root.Left) - height(root.Right)) <= 1 && isBalanced(root.Left) && isBalanced(root.Right)
}

func height(root *TreeNode) int {
    if root == nil {
        return 0
    }
    return max(height(root.Left), height(root.Right)) + 1
}

func max(x, y int) int {
    if x > y {
        return x
    }
    return y
}

func abs(x int) int {
    if x < 0 {
        return -1 * x
    }
    return x
}

复杂度分析

  • 时间复杂度:$O(n^2)$,其中 $n$ 是二叉树中的节点个数。
    最坏情况下,二叉树是满二叉树,需要遍历二叉树中的所有节点,时间复杂度是 $O(n)$。
    对于节点 $p$,如果它的高度是 $d$,则 $\texttt{height}(p)$ 最多会被调用 $d$ 次(即遍历到它的每一个祖先节点时)。对于平均的情况,一棵树的高度 $h$ 满足 $O(h)=O(\log n)$,因为 $d \leq h$,所以总时间复杂度为 $O(n \log n)$。对于最坏的情况,二叉树形成链式结构,高度为 $O(n)$,此时总时间复杂度为 $O(n^2)$。

  • 空间复杂度:$O(n)$,其中 $n$ 是二叉树中的节点个数。空间复杂度主要取决于递归调用的层数,递归调用的层数不会超过 $n$。

方法二:自底向上的递归

方法一由于是自顶向下递归,因此对于同一个节点,函数 $\texttt{height}$ 会被重复调用,导致时间复杂度较高。如果使用自底向上的做法,则对于每个节点,函数 $\texttt{height}$ 只会被调用一次。

自底向上递归的做法类似于后序遍历,对于当前遍历到的节点,先递归地判断其左右子树是否平衡,再判断以当前节点为根的子树是否平衡。如果一棵子树是平衡的,则返回其高度(高度一定是非负整数),否则返回 $-1$。如果存在一棵子树不平衡,则整个二叉树一定不平衡。

<fig1,fig2,fig3,fig4,fig5,fig6,fig7,fig8,fig9,fig10,fig11,fig12,fig13,fig14,fig15,fig16,fig17,fig18,fig19,fig20,fig21,fig22,fig23,fig24,fig25,fig26,fig27,fig28,fig29,fig30,fig31,fig32>

###Java

class Solution {
    public boolean isBalanced(TreeNode root) {
        return height(root) >= 0;
    }

    public int height(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftHeight = height(root.left);
        int rightHeight = height(root.right);
        if (leftHeight == -1 || rightHeight == -1 || Math.abs(leftHeight - rightHeight) > 1) {
            return -1;
        } else {
            return Math.max(leftHeight, rightHeight) + 1;
        }
    }
}

###C++

class Solution {
public:
    int height(TreeNode* root) {
        if (root == NULL) {
            return 0;
        }
        int leftHeight = height(root->left);
        int rightHeight = height(root->right);
        if (leftHeight == -1 || rightHeight == -1 || abs(leftHeight - rightHeight) > 1) {
            return -1;
        } else {
            return max(leftHeight, rightHeight) + 1;
        }
    }

    bool isBalanced(TreeNode* root) {
        return height(root) >= 0;
    }
};

###Python

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        def height(root: TreeNode) -> int:
            if not root:
                return 0
            leftHeight = height(root.left)
            rightHeight = height(root.right)
            if leftHeight == -1 or rightHeight == -1 or abs(leftHeight - rightHeight) > 1:
                return -1
            else:
                return max(leftHeight, rightHeight) + 1

        return height(root) >= 0

###C

int height(struct TreeNode* root) {
    if (root == NULL) {
        return 0;
    }
    int leftHeight = height(root->left);
    int rightHeight = height(root->right);
    if (leftHeight == -1 || rightHeight == -1 || fabs(leftHeight - rightHeight) > 1) {
        return -1;
    } else {
        return fmax(leftHeight, rightHeight) + 1;
    }
}

bool isBalanced(struct TreeNode* root) {
    return height(root) >= 0;
}

###golang

func isBalanced(root *TreeNode) bool {
    return height(root) >= 0
}

func height(root *TreeNode) int {
    if root == nil {
        return 0
    }
    leftHeight := height(root.Left)
    rightHeight := height(root.Right)
    if leftHeight == -1 || rightHeight == -1 || abs(leftHeight - rightHeight) > 1 {
        return -1
    }
    return max(leftHeight, rightHeight) + 1
}

func max(x, y int) int {
    if x > y {
        return x
    }
    return y
}

func abs(x int) int {
    if x < 0 {
        return -1 * x
    }
    return x
}

复杂度分析

  • 时间复杂度:$O(n)$,其中 $n$ 是二叉树中的节点个数。使用自底向上的递归,每个节点的计算高度和判断是否平衡都只需要处理一次,最坏情况下需要遍历二叉树中的所有节点,因此时间复杂度是 $O(n)$。

  • 空间复杂度:$O(n)$,其中 $n$ 是二叉树中的节点个数。空间复杂度主要取决于递归调用的层数,递归调用的层数不会超过 $n$。

使字符串平衡的最少删除次数

2023年3月4日 18:36

方法一:枚举

思路

通过删除部分字符串,使得字符串达到下列三种情况之一,即为平衡状态:

  1. 字符串全为 $\text{``a''}$;
  2. 字符串全为 $\text{``b''}$;
  3. 字符串既有 $\text{a''}$ 也有 $\text{b''}$,且所有 $\text{a''}$ 都在所有 $\text{b''}$ 左侧。

其中,为了达到第 $1$ 种情况,最少需要删除所有的 $\text{b''}$。为了达到第 $2$ 种情况,最少需要删除所有的 $\text{a''}$。而第 $3$ 种情况,可以在原字符串相邻的两个字符之间划一条间隔,删除间隔左侧所有的 $\text{b''}$ 和间隔右侧所有的 $\text{a''}$ 即可达到。用 $\textit{leftb}$ 表示间隔左侧的 $\text{b''}$ 的数目,$\textit{righta}$ 表示间隔左侧的 $\text{a''}$ 的数目,$\textit{leftb}+\textit{righta}$ 即为当前划分的间隔下最少需要删除的字符数。这样的间隔一共有 $n-1$ 种,其中 $n$ 是 $s$ 的长度。遍历字符串 $s$,即可以遍历 $n-1$ 种间隔,同时更新 $\textit{leftb}$ 和 $\textit{righta}$ 的数目。而上文讨论的前两种情况,其实就是间隔位于首字符前和末字符后的两种特殊情况,可以加入第 $3$ 种情况一并计算。

代码

###Python

class Solution:
    def minimumDeletions(self, s: str) -> int:
        leftb, righta = 0, s.count('a')
        res = righta
        for c in s:
            if c == 'a':
                righta -= 1
            else:
                leftb += 1
            res = min(res, leftb + righta)
        return res

###Java

class Solution {
    public int minimumDeletions(String s) {
        int leftb = 0, righta = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == 'a') {
                righta++;
            }
        }
        int res = righta;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == 'a') {
                righta--;
            } else {
                leftb++;
            }
            res = Math.min(res, leftb + righta);
        }
        return res;
    }
}

###C#

public class Solution {
    public int MinimumDeletions(string s) {
        int leftb = 0, righta = 0;
        foreach (char c in s) {
            if (c == 'a') {
                righta++;
            }
        }
        int res = righta;
        foreach (char c in s) {
            if (c == 'a') {
                righta--;
            } else {
                leftb++;
            }
            res = Math.Min(res, leftb + righta);
        }
        return res;
    }
}

###C++

class Solution {
public:
    int minimumDeletions(string s) {
        int leftb = 0, righta = 0;
        for (int i = 0; i < s.size(); i++) {
            if (s[i] == 'a') {
                righta++;
            }
        }
        int res = righta;
        for (int i = 0; i < s.size(); i++) {
            char c = s[i];
            if (c == 'a') {
                righta--;
            } else {
                leftb++;
            }
            res = min(res, leftb + righta);
        }
        return res;
    }
};

###C

#define MIN(a, b) ((a) < (b) ? (a) : (b))

int minimumDeletions(char * s) {
    int len = strlen(s);
    int leftb = 0, righta = 0;
    for (int i = 0; i < len; i++) {
        if (s[i] == 'a') {
            righta++;
        }
    }
    int res = righta;
    for (int i = 0; i < len; i++) {
        char c = s[i];
        if (c == 'a') {
            righta--;
        } else {
            leftb++;
        }
        res = MIN(res, leftb + righta);
    }
    return res;
}

###JavaScript

var minimumDeletions = function(s) {
    let leftb = 0, righta = 0;
    for (let i = 0; i < s.length; i++) {
        if (s[i] === 'a') {
            righta++;
        }
    }
    let res = righta;
    for (let i = 0; i < s.length; i++) {
        const c = s[i];
        if (c === 'a') {
            righta--;
        } else {
            leftb++;
        }
        res = Math.min(res, leftb + righta);
    }
    return res;
};

###go

func minimumDeletions(s string) int {
    leftb := 0
    righta := 0
    for _, c := range s {
        if c == 'a' {
            righta++
        }
    }
    res := righta
    for _, c := range s {
        if c == 'a' {
            righta--
        } else {
            leftb++
        }
        res = min(res, leftb+righta)
    }
    return res
}

func min(a, b int) int {
    if a > b {
        return b
    }
    return a
}

复杂度分析

  • 时间复杂度:$O(n)$,其中 $n$ 是 $s$ 的长度。需要遍历两遍 $s$,第一遍计算出 $s$ 中 $\text{``a''}$ 的数量,第二遍遍历所有的间隔,求出最小需要删除的字符数。

  • 空间复杂度:$O(1)$,只需要常数空间。

❌
❌