Hard

题目描述

数组中的峰值是指比其前一个和后一个元素都大的元素。

给你一个整数数组 nums 和一个二维整数数组 queries

你需要处理两种类型的查询:

  • queries[i] = [1, li, ri]:确定子数组 nums[li..ri] 中峰值元素的数量。
  • queries[i] = [2, indexi, vali]:将 nums[indexi] 更改为 vali

返回一个数组 answer,按顺序包含第一种类型查询的结果。

注意:

  • 数组或子数组的第一个和最后一个元素不能成为峰值。

示例 1:

输入:nums = [3,1,4,2,5], queries = [[2,3,4],[1,0,4]]
输出:[0]
解释:
第一个查询:我们将 nums[3] 改为 4,nums 变为 [3,1,4,4,5]。
第二个查询:[3,1,4,4,5] 中的峰值数量为 0。

示例 2:

输入:nums = [4,1,4,2,1,5], queries = [[2,2,4],[1,0,2],[1,0,4]]
输出:[0,1]
解释:
第一个查询:nums[2] 应该变为 4,但它已经是 4 了。
第二个查询:[4,1,4] 中的峰值数量为 0。
第三个查询:[4,1,4,2,1] 中第二个 4 是峰值。

提示:

  • 3 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^5
  • 1 <= queries.length <= 10^5
  • queries[i][0] == 1 or queries[i][0] == 2
  • 对于 queries[i][0] == 10 <= queries[i][1] <= queries[i][2] <= nums.length - 1
  • 对于 queries[i][0] == 20 <= queries[i][1] <= nums.length - 1, 1 <= queries[i][2] <= 10^5

解题思路

这是一道涉及动态数组修改和区间查询的问题,需要使用高效的数据结构来维护峰值信息。

核心思路:

  1. 峰值判断:位置 i 是峰值当且仅当 nums[i] > nums[i-1] && nums[i] > nums[i+1],且 1 <= i <= n-2

  2. 影响范围分析:当修改位置 i 的值时,只会影响位置 i-1, i, i+1 三个位置的峰值状态

  3. 数据结构选择:使用树状数组(Binary Indexed Tree)或线段树来维护峰值的前缀和,支持:

    • 单点更新:O(log n)
    • 区间求和:O(log n)

算法流程:

  1. 初始化一个标记数组 isPeak,记录每个位置是否为峰值
  2. 建立树状数组,维护峰值的前缀和
  3. 对于每个查询:
    • 类型1(区间查询):查询范围 [l+1, r-1] 的峰值总数
    • 类型2(单点修改):更新nums[index],重新计算 index-1, index, index+1 的峰值状态,并更新树状数组

优化要点:

  • 查询范围需要排除边界元素,因为它们不能成为峰值
  • 修改操作需要先撤销旧状态,再添加新状态

代码实现

class Solution {
public:
    class BIT {
        vector<int> tree;
        int n;
    public:
        BIT(int size) : n(size) {
            tree.resize(n + 1, 0);
        }
        
        void update(int i, int delta) {
            for (i++; i <= n; i += i & -i) {
                tree[i] += delta;
            }
        }
        
        int query(int i) {
            int sum = 0;
            for (i++; i > 0; i -= i & -i) {
                sum += tree[i];
            }
            return sum;
        }
        
        int rangeQuery(int l, int r) {
            if (l > r) return 0;
            return query(r) - (l > 0 ? query(l - 1) : 0);
        }
    };
    
    bool isPeak(vector<int>& nums, int i) {
        if (i <= 0 || i >= nums.size() - 1) return false;
        return nums[i] > nums[i - 1] && nums[i] > nums[i + 1];
    }
    
    vector<int> countOfPeaks(vector<int>& nums, vector<vector<int>>& queries) {
        int n = nums.size();
        BIT bit(n);
        vector<bool> peaks(n, false);
        
        for (int i = 1; i < n - 1; i++) {
            if (isPeak(nums, i)) {
                peaks[i] = true;
                bit.update(i, 1);
            }
        }
        
        vector<int> result;
        for (auto& query : queries) {
            if (query[0] == 1) {
                int l = query[1], r = query[2];
                result.push_back(bit.rangeQuery(l + 1, r - 1));
            } else {
                int idx = query[1], val = query[2];
                
                for (int i = max(1, idx - 1); i <= min(n - 2, idx + 1); i++) {
                    if (peaks[i]) {
                        bit.update(i, -1);
                        peaks[i] = false;
                    }
                }
                
                nums[idx] = val;
                
                for (int i = max(1, idx - 1); i <= min(n - 2, idx + 1); i++) {
                    if (isPeak(nums, i)) {
                        peaks[i] = true;
                        bit.update(i, 1);
                    }
                }
            }
        }
        
        return result;
    }
};
class Solution:
    def countOfPeaks(self, nums: List[int], queries: List[List[int]]) -> List[int]:
        class BIT:
            def __init__(self, n):
                self.n = n
                self.tree = [0] * (n + 1)
            
            def update(self, i, delta):
                i += 1
                while i <= self.n:
                    self.tree[i] += delta
                    i += i & (-i)
            
            def query(self, i):
                i += 1
                res = 0
                while i > 0:
                    res += self.tree[i]
                    i -= i & (-i)
                return res
            
            def range_query(self, l, r):
                if l > r:
                    return 0
                return self.query(r) - (self.query(l - 1) if l > 0 else 0)
        
        def is_peak(i):
            if i <= 0 or i >= len(nums) - 1:
                return False
            return nums[i] > nums[i - 1] and nums[i] > nums[i + 1]
        
        n = len(nums)
        bit = BIT(n)
        peaks = [False] * n
        
        for i in range(1, n - 1):
            if is_peak(i):
                peaks[i] = True
                bit.update(i, 1)
        
        result = []
        for query in queries:
            if query[0] == 1:
                l, r = query[1], query[2]
                result.append(bit.range_query(l + 1, r - 1))
            else:
                idx, val = query[1], query[2]
                
                for i in range(max(1, idx - 1), min(n - 1, idx + 2)):
                    if peaks[i]:
                        bit.update(i, -1)
                        peaks[i] = False
                
                nums[idx] = val
                
                for i in range(max(1, idx - 1), min(n - 1, idx + 2)):
                    if is_peak(i):
                        peaks[i] = True
                        bit.update(i, 1)
        
        return result
public class Solution {
    public class BIT {
        private int[] tree;
        private int n;
        
        public BIT(int size) {
            n = size;
            tree = new int[n + 1];
        }
        
        public void Update(int i, int delta) {
            for (i++; i <= n; i += i & -i) {
                tree[i] += delta;
            }
        }
        
        public int Query(int i) {
            int sum = 0;
            for (i++; i > 0; i -= i & -i) {
                sum += tree[i];
            }
            return sum;
        }
        
        public int RangeQuery(int l, int r) {
            if (l > r) return 0;
            return Query(r) - (l > 0 ? Query(l - 1) : 0);
        }
    }
    
    private bool IsPeak(int[] nums, int i) {
        if (i <= 0 || i >= nums.Length - 1) return false;
        return nums[i] > nums[i - 1] && nums[i] > nums[i + 1];
    }
    
    public IList<int> CountOfPeaks(int[] nums, int[][] queries) {
        int n = nums.Length;
        BIT bit = new BIT(n);
        bool[] peaks = new bool[n];
        
        for (int i = 1; i < n - 1; i++) {
            if (IsPeak(nums, i)) {
                peaks[i] = true;
                bit.Update(i, 1);
            }
        }
        
        List<int> result = new List<int>();
        foreach (var query in queries) {
            if (query[0] == 1) {
                int l = query[1], r = query[2];
                result.Add(bit.RangeQuery(l + 1, r - 1));
            } else {
                int idx = query[1], val = query[2];
                
                for (int i = Math.Max(1, idx - 1); i <= Math.Min(n - 2, idx + 1); i++) {
                    if (peaks[i]) {
                        bit.Update(i, -1);
                        peaks[i] = false;
                    }
                }
                
                nums[idx] = val;
                
                for (int i = Math.Max(1, idx - 1); i <= Math.Min(n - 2, idx + 1); i++) {
                    if (IsPeak(nums, i)) {
                        peaks[i] = true;
                        bit.Update(i, 1);
                    }
                }
            }
        }
        
        return result;
    }
}
var countOfPeaks = function(nums, queries) {
    class BIT {
        constructor(n) {
            this.n = n;
            this.tree = new Array(n + 1).fill(0);
        }
        
        update(i, delta) {
            for (i++; i <= this.n; i += i & -i) {
                this.tree[i] += delta;
            }
        }
        
        query(i) {
            let sum = 0;
            for (i++; i > 0; i -= i & -i) {
                sum += this.tree[i];
            }
            return sum;
        }
        
        rangeQuery(l, r) {
            if (l > r) return 0;
            return this.query(r) - (l > 0 ? this.query(l - 1) : 0);
        }
    }
    
    function isPeak(i) {
        if (i <= 0 || i >= nums.length - 1) return false;
        return nums[i] > nums[i - 1] && nums[i] > nums[i + 1];
    }
    
    const n = nums.length;
    const bit = new BIT(n);
    const peaks = new Array(n).fill(false);
    
    for (let i = 1; i < n - 1; i++) {
        if (isPeak(i)) {
            peaks[i] = true;
            bit.update(i, 1);
        }
    }
    
    const result = [];
    for (const query of queries) {
        if (query[0]

复杂度分析

操作时间复杂度空间复杂度
预处理O(n log n)O(n)
单次查询O(log n)O(1)
单次修改O(log n)O(1)
总时间复杂度O((n + m) log n)O(n)

其中 n 为数组长度,m 为查询数量。树状数组的空间复杂度为 O(n),时间复杂度为单次操作 O(log n)。