刷题-稀疏表ST解决区间最值问题

98 阅读2分钟

题目链接

leetcode.cn/problems/sl…

题目

image.png

解题过程

step1:将数据采用稀疏表进行预处理,时间复杂度O(nlogn)

step2:查找区间内的最值,时间复杂度O(1)

稀疏表预处理讲解

稀疏表ST结合了动态规划倍增的思想。

定义dp[i,j] 表示[i,i+2^j-1]区间的数据。

[i,i+2^j-1] 可以均分为 [i,i+2^(j-1)-1][i+2^(j-1),i+2^(j-1)++2^(j-1)-1]两个区间。

  • [i,i+2^(j-1)-1] = dp[i][j-1]
  • [i+2^(j-1),i+2^(j-1)+2^(j-1)-1] = dp[i+2^(j-1)][j-1]

所以: dp[i,j] = dp[i][j-1] + dp[i+2^(j-1)][j-1]

举例说明ST的实现过程:

image.png

查找过程

对于任意查找区间[l,r],都可以将其分为满足以下条件的两个区间:

  1. 两个区间范围 覆盖查找区间。
  2. 拆分区间刚好在 预处理的dp 中。即,拆分的区间[left_p,right_p] 满足right_p = left_p + 2^m - 1。

如何拆分呢?

找到m,使其满足2^m <= r-l+1 < 2^(m+1)。拆分后的两个区间为:[l,l+2^m-1] [r-2^m+1,r],对应稀疏表的dp[l][m]dp[r-2^m+1][m].

求出以上两个区间的最值即可得出[l,r]区间的最值。

例如:

[3,8]区间的m=2,其拆分的区间为:[3,6] 和 [5,8], 只需要计算dp[3][2] 和 dp[5][2]的最值即可。

image.png

JAVA实现过程

以给出的题目为例,其java实现代码如下:

public int[] maxSlidingWindow(int[] nums, int k) {
        // 采用ST预处理数据
        int n = logarithm(nums.length);
        int[][] dp = new int[nums.length][n + 1];
        // 初始数据
        for (int i = 0; i < nums.length; i++) {
            dp[i][0] = nums[i];
        }
        // 按照状态转移方程计算
        for (int j = 1; j < n + 1; j++) {
            for (int i = 0; i < nums.length; i++) {
                if (i + power(j - 1) < nums.length) {
                    dp[i][j] = max(dp[i][j - 1], dp[i + power(j - 1)][j - 1]);
                }
            }
        }

        // 查询数据
        int kn = logarithm(k);
        int[] res = new int[nums.length - k + 1];
        for (int l = 0; l < nums.length - k + 1; l++) {
            int r = l + k - 1;
            // 计算区间结果
            res[l] = max(dp[l][kn], dp[r - power(kn) + 1][kn]);
        }

        return res;
    }

    /**
     * 求2的a次方
     * 
     * @param a a为正整数 或者 0
     * @return
     */
    public int power(int a) {
        int num = 1;
        for (int i = 0; i < a; i++) {
            num *= 2;
        }

        return num;
    }

    /**
     * 求两数之间较大值
     * 
     * @param a
     * @param b
     * @return
     */
    public int max(int a, int b) {
        if (a > b) {
            return a;
        }
        return b;
    }

    /**
     * 求以2为低,target的对数
     * 
     * @param target
     * @return
     */
    public int logarithm(int target) {
        if (target == 1) {
            return 0;
        }

        int c = 0;
        int num = 1;
        while (true) {
            if (num > target) {
                break;
            }
            num *= 2;
            c++;
        }

        return --c;
    }