题目来源:力扣
题目介绍
如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列。
========================================================
示例:
A = [1, 2, 3, 4]
返回: 3, A 中有三个子等差数组: [1, 2, 3], [2, 3, 4] 以及自身 [1, 2, 3, 4]。
=======================================================
审题:
对于这道题, 我们首先提供一种易于理解, 但时间复杂度较高的方法.
我们可以计算出所有子序列S[i, j]是否是等差数列, 然后累计其中为等差数列的个数. 如果我们已知子序列S[i, j]是否是等差数列, 则不能判断子序列S[i, j+1]是否是等差数列. 因此, 我们可以先计算所有长度为3的子序列是否是等差数列, 然后一次计算所有长度为4, …5, n的子序列是否是等差数列.
class Solution {
public int numberOfArithmeticSlices(int[] A) {
boolean[][] S = new boolean[A.length][A.length];
int total = 0;
//basecase , 判断长度为3的数列是否是等差数列
for(int i = 0; i < A.length-2; i++){
if(A[i+1]-A[i] == A[i+2]-A[i+1]){
S[i][i+2] = true;
total++;
}
}
for(int len = 4; len <= A.length; len++){
for(int i = 0; i <= A.length-len; i++){
int j = i + len -1;
if(S[i][j-1] && A[j]-A[j-1] == A[j-1]-A[j-2]){
S[i][j] = true;
total++;
}
else
S[i][j] = false;
}
}
return total;
}
}
复杂度分析
不难看出, 该方法时间复杂度与空间复杂度均为.
实际上, 为了计算所有等差数列的个数, 我们可以优化上述代码, 将空间复杂度由降为. 如果当前A[i, j]为等差数列, 则我们可以往下判断A[i, j+1]是否是等差数列, 如果A[i, j]不是等差数列, 则A[i, j+1], …A[i, n]都不是等差数列.
class Solution {
public int numberOfArithmeticSlices(int[] A) {
int total = 0;
for(int i = 0; i < A.length-2; i++){
for(int len = 2; len < A.length-i; len++){
if(A[i+len]-A[i+len-1] == A[i+len-1] - A[i+len-2])
total++;
else
break;
}
}
return total;
}
}
动态规划算法
考虑, 如果我们已知以A[i]结尾的等差数列的个数, 我们能否推导出以A[i+1]结尾的等差数列的个数. 事实上非常容易, 如果A[i+1] - A[i] = A[i]-A[i-1], 那么所有以A[i+1]结尾的等差数列此时在增加了一个元素A[i+1]后仍然是等差数列, 而A[i-1], A[i], A[i+1]此时也构长一个等差数列, 因此以A[i+1]结尾的等差数列的个数等于以A[i]结尾的等差数列的个数+1. 而如果A[i+1]-A[i] != A[i]-A[i-1], 则以A[i+1]结尾的等差数列个数为1.
我们不难写出该动态规划算法的实现:
class Solution {
public int numberOfArithmeticSlices(int[] A) {
//S[j]表示以j结尾的等差序列个数
//如果A[j+1] - A[j] = A[j] - A[j-1] , S[j+1] = S[j] + 1
//else S[j+1] = 0;
if(A.length < 3)
return 0;
int total = 0;
int[] S = new int[A.length];
if(A[2]-A[1] == A[1]-A[0]){
S[2] = 1;
total += S[2];
}
for(int i = 3; i < A.length; i++){
if(A[i] - A[i-1] == A[i-1] - A[i-2]){
S[i] = S[i-1] + 1;
total += S[i];
}
}
return total;
}
}
不难分析发现, 该算法的时间复杂度与空间复杂度均为.由于以A[i+1]结尾的等差数列个数只有以A[i]结尾的等差数列个数相关, 因此我们可以考虑进行状态压缩, 将空间复杂度降为
class Solution {
public int numberOfArithmeticSlices(int[] A) {
if(A.length < 3)
return 0;
int total = 0;
int prev = 0;
if(A[2]-A[1] == A[1]-A[0]){
prev = 1;
total += prev;
}
for(int i = 3; i < A.length; i++){
if(A[i] - A[i-1] == A[i-1] - A[i-2]){
prev = prev + 1;
total += prev;
}
else
prev = 0;
}
return total;
}
}