Leetcode 413. 等差数列划分

413. 等差数列划分

问题描述

如果一个数列 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该数列为等差数列。

  • 例如,[1,3,5,7,9][7,7,7,7][3,-1,-5,-9] 都是等差数列。

给你一个整数数组 nums ,返回数组 nums 中所有为等差数组的 子数组 个数。

子数组 是数组中的一个连续序列。

示例 1:

输入:nums = [1,2,3,4]
输出:3
解释:nums 中有三个子等差数组:[1, 2, 3]、[2, 3, 4] 和 [1,2,3,4] 自身。

示例 2:

输入:nums = [1]
输出:0

提示:

  • 1 <= nums.length <= 5000
  • -1000 <= nums[i] <= 1000

解题思路与代码实现

使用滑动窗口+动态规划:

滑动窗口:求给定数组是否存在长度大于等于3的等差数列

动态规划:求解长度为n(n>=3)的等差数列所拥有的子数组的个数;

​ 边界条件:dp[1] = 0, dp[2] = 3

​ 状态转移方程: dp[i] = dp[i-1] + i-2

class Solution {
    
    /**
         * 如果一个数列 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该数列为等差数列。
         * 利用滑动窗口 找到数组中的等差数列,并将它们的长度保存到list返回
         *
         * @param nums
         * @return
         */
        private List<Integer> partion(int[] nums) {
            int left = 0, right = 1;
            int d = nums[1] - nums[0];
            List<Integer> result = new ArrayList<>();
            while (left < nums.length && right < nums.length) {
                // 如果当前窗口内的元素能组成等差数列
                if (nums[right - 1] + d == nums[right]) {
                    right++;    // 右边界扩张
                    // 右移后右边界如果越界,说明数组已经被遍历一遍了
                    if (right >= nums.length) {
                        // 判断当前窗口大小(right-left)是否大于等于3
                        if (right - left >= 3) {
                            result.add(right - left);
                        }
                    }
                } else {
                    // 判断当前窗口大小(right-left)是否大于等于3
                    if (right - left >= 3) {
                        result.add(right - left);
                    }
                    // 左边界收缩
                    left = right - 1;
                    // 更新公差d
                    d = nums[right] - nums[left];
                }
            }
            // 返回结果
            return result;
        }
    
        public int numberOfArithmeticSlices(int[] nums) {
            // 长度小于3的数组不会存在等差数列,直接返回0
            if (nums.length < 3) {
                return 0;
            }
            List<Integer> list = partion(nums);
            // 如果list为空,说明数组中并不存在连续的等差数列,直接返回0
            if (list.isEmpty()) {
                return 0;
            }
            Integer max = list.stream().max(Comparator.naturalOrder()).get();
            // max<= nums.length,节约部分空间
            int[] dp = new int[max + 1];
            // dp[i]表示长度为i的等差数列所包含的子数组 个数
            for (int i = 1; i < dp.length; i++) {
                if (i <= 2) {
                    // 边界条件
                    dp[i] = 0;
                } else {
                    // 状态转移方程
                    dp[i] = dp[i - 1] + (i - 2);
                }
            }
            // return dp[nums.length];
            int result = 0;
            for (Integer i : list) {
                result += dp[i];
            }
            return result;

        }

        
        
    }

数学规律:在本题中,长度为n的等差数列会比长度为n-1的等差数列的子数组个数多n-2个(1…n, …, n-2…n),即有 f(n) = f(n-1) + n-2;而f(3) = 1, 据此可以算出长度为n(n>=3)的等差数列子数组个数:
f(n) = f(n-1) + n-2 = f(1) + 2 + 3 + … + n-2
= (1+n-2)*(n-2)/2
=(n^2-3n+2)/2

所以,简化numberOfArithmeticSlices代码

public int numberOfArithmeticSlices2(int[] nums) {
            // 长度小于3的数组不会存在等差数列,直接返回0
            if (nums.length < 3) {
                return 0;
            }
            List<Integer> list = partion(nums);
            // 如果list为空,说明数组中并不存在连续的等差数列,直接返回0
            if (list.isEmpty()) {
                return 0;
            }
            int result = 0;
            for (Integer i : list) {
                result += (i * i - 3 * i + 2) / 2;
            }
            return result;    
}

你可能感兴趣的:(leetcode,leetcode,算法,职场和发展,动态规划)