作者: 负雪明烛
id: fuxuemingzhu
个人博客: http://fuxuemingzhu.cn/


题目地址:https://leetcode.com/problems/arithmetic-slices/description/

题目描述

A sequence of number is called arithmetic if it consists of at least three elements and if the difference between any two consecutive elements is the same.

For example, these are arithmetic sequence:

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9
The following sequence is not arithmetic. 1, 1, 2, 5, 7

A zero-indexed array A consisting of N numbers is given. A slice of that array is any pair of integers (P, Q) such that 0 <= P < Q < N.

A slice (P, Q) of array A is called arithmetic if the sequence:
A[P], A[p + 1], …, A[Q - 1], A[Q] is arithmetic. In particular, this means that P + 1 < Q.

The function should return the number of arithmetic slices in the array A.

Example:

A = [1, 2, 3, 4]

return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] itself.

题目大意

这道题的题目不是一般的长,其实就是一个意思:给你一串数字,返回这串数字中能够构成等差数列的子串的数目。

解题方法

本题要我们求数组中有多少个等差数列。

本题解分成了 4 个部分:暴力 => 双指针 => 递归 => 动态规划

暴力

最容易想到的就是暴力解法:判断所有的子数组是不是等差数列,如果是的话就累加次数。

C++ 代码如下:

class Solution {
public:
int numberOfArithmeticSlices(vector<int>& A) {
const int N = A.size();
int res = 0;
for (int i = 0; i < N - 2; i++) {
for (int j = i + 1; j < N; j++) {
if (isArithmetic(A, i, j))
res ++;
}
}
return res;
}
private:
bool isArithmetic(vector<int>& A, int start, int end) {
if (end - start < 2) return false;
for (int i = start; i < end - 1; i++) {
if (A[i + 1] * 2 != A[i] + A[i + 2])
return false;
}
return true;
}
};
  • 时间复杂度:

    O

    (

    N

    3

    )

    O(N ^ 3)

    O(N3),遍历所有的子数组,需要有两重循环,时间复杂度是

    O

    (

    N

    2

    )

    O(N^2)

    O(N2);判断每个子数组是不是等差数列,时间复杂度是

    O

    (

    N

    )

    O(N)

    O(N);所以总的时间复杂度是

    O

    (

    N

    3

    )

    O(N ^ 3)

    O(N3) 。

  • 空间复杂度:

    O

    (

    1

    )

    O(1)

    O(1)。

双指针

在上面的暴力解法中,我们对每个子数组都进行了是否为等差数列的判断。

  • 其实,如果我们已经知道一个子数组的前面部分不是等差数列以后,那么后面部分就不用判断了。
  • 同时,我们知道等差数列的所有的相邻数字的差是固定的。

因此,对于每个起始位置,我们只需要向后进行一遍扫描,直到不再构成等差数列为止,此时已经没有必要再向后扫描。

这个思路其实就是双指针(滑动窗口) 的解法。

C++ 代码如下,

class Solution {
public:
int numberOfArithmeticSlices(vector<int>& A) {
const int N = A.size();
int res = 0;
for (int i = 0; i < N - 2; i++) {
int d = A[i + 1] - A[i];
for (int j = i + 1; j < N - 1; j++) {
if (A[j + 1] - A[j] == d)
res ++;
else
break;
}
}
return res;
}
};
  • 时间复杂度:

    O

    (

    N

    2

    )

    O(N^2)

    O(N2);

  • 空间复杂度:

    O

    (

    1

    )

    O(1)

    O(1)。

递归

从上面的思路中,我们已经逐渐的抽象出一个思路了:固定起点,判断后面的等差数列有多少个

类似的思路,我们可以构造出「自顶向下」的递归解法:定义递归函数 slices(A, end)的含义是区间 A[0, end] 中,end 作为终点的,等差数列的个数。

A[0, end]内的end作为终点的等差数列的个数,相当于在 A[0, end - 1]的基础上,增加了 A[end]

有两种情况:

  1. A[end] - A[end - 1] == A[end - 1] - A[end - 2]时,说明增加的A[end]能和前面构成等差数列,那么 slices(A, end) = slices(A, end - 1) + 1
  2. A[end] - A[end - 1] != A[end - 1] - A[end - 2]时, 说明增加的 A[end]不能和前面构成等差数列,所以slices(A, end) = 0;

最后,我们要求的是整个数组中的等差数列的数目,所以需要把

0

<

=

e

n

d

<

=

l

e

n

(

A

1

)

0 <= end <= len(A - 1)

0<=end<=len(A−1) 的所有递归函数的结果累加起来。

class Solution(object):
def numberOfArithmeticSlices(self, A):
N = len(A)
self.res = 0
self.slices(A, N - 1)
return self.res def slices(self, A, end):
if end < 2: return 0
op = 0
if A[end] - A[end - 1] == A[end - 1] - A[end - 2]:
op = 1 + self.slices(A, end - 1)
self.res += op
else:
self.slices(A, end - 1)
return op
  • 时间复杂度:

    O

    (

    N

    2

    )

    O(N^2)

    O(N2)。因为递归函数最多被调用 N 次,每次调用的时候都需要向前遍历一次。

  • 空间复杂度:

    O

    (

    N

    )

    O(N)

    O(N),递归栈的最大深度是 N。

动态规划

上面的递归的解法,是「自顶向下」的思路。如果转成「自底向上」的思路,就变成了动态规划。

类似于递归解法,我们定义

d

p

[

i

]

dp[i]

dp[i] 是以

A

[

i

]

A[i]

A[i] 为终点的等差数列的个数

类似于上面的递归思路,有两种情况:

  1. A[i] - A[i - 1] == A[i - 1] - A[i - 2]时,说明增加的A[i]能和前面构成等差数列,那么 dp[i] = dp[i - 1] + 1
  2. A[i] - A[i - 1] != A[i - 1] - A[i - 2]时, 说明增加的 A[i]不能和前面构成等差数列,所以dp[i] = 0;

动态规划的初始状态:

d

p

[

0

]

=

0

,

d

p

[

1

]

=

0

dp[0] = 0, dp[1] = 0

dp[0]=0,dp[1]=0。

最后,我们要求的是整个数组中的等差数列的数目,所以需要把

0

<

=

i

<

=

l

e

n

(

A

1

)

0 <= i <= len(A - 1)

0<=i<=len(A−1) 的所有

d

p

[

i

]

dp[i]

dp[i] 的结果累加起来。

class Solution(object):
def numberOfArithmeticSlices(self, A):
N = len(A)
dp = [0] * N
for i in range(1, N - 1):
if A[i - 1] + A[i + 1] == A[i] * 2:
dp[i] = dp[i - 1] + 1
return sum(dp)
  • 时间复杂度:

    O

    (

    N

    )

    O(N)

    O(N);

  • 空间复杂度:

    O

    (

    N

    )

    O(N)

    O(N);

由于 dp[i] 只和 dp[i - 1] 有关,所以可以进行状态压缩,只用一个变量 k 来表示以

A

[

i

]

A[i]

A[i] 为终点的等差数列的个数

计算的方式仍然不变。

class Solution(object):
def numberOfArithmeticSlices(self, A):
count = 0
k = 0
for i in xrange(len(A) - 2):
if A[i + 1] - A[i] == A[i + 2] - A[i + 1]:
k += 1
count += k
else:
k = 0
return count
  • 时间复杂度:

    O

    (

    N

    )

    O(N)

    O(N);

  • 空间复杂度:

    O

    (

    1

    )

    O(1)

    O(1);

日期

2018 年 2 月 28 日
2018 年 12 月 10 日 —— 又是周一!
2021 年 8 月 10 日 —— 昨天开车去了雁栖湖

【LeetCode】413. Arithmetic Slices 等差数列划分的更多相关文章

  1. 413 Arithmetic Slices 等差数列划分

    如果一个数列至少有三个元素,并且任意两个相邻元素之差相同,则称该数列为等差数列.例如,以下数列为等差数列:1, 3, 5, 7, 97, 7, 7, 73, -1, -5, -9以下数列不是等差数列. ...

  2. LeetCode 413 Arithmetic Slices详解

    这个开始自己做的动态规划复杂度达到了O(n), 是用的是2维的矩阵来存前面的数据,复杂度太高了, 虽然好理解,但是没效率,后面看这个博客发现没有动态规划做了这个题 也是比较厉害. 转载地址: http ...

  3. LN : leetcode 413 Arithmetic Slices

    lc 413 Arithmetic Slices 413 Arithmetic Slices A sequence of number is called arithmetic if it consi ...

  4. [LeetCode]413 Arithmetic Slices

    A sequence of number is called arithmetic if it consists of at least three elements and if the diffe ...

  5. LeetCode - 413. Arithmetic Slices - 含中文题意解释 - O(n) - ( C++ ) - 解题报告

    1.题目大意 A sequence of number is called arithmetic if it consists of at least three elements and if th ...

  6. Week 8 - 338.Counting Bits & 413. Arithmetic Slices

    338.Counting Bits - Medium Given a non negative integer number num. For every numbers i in the range ...

  7. Leetcode 413. Arithmetic Slice 算术序列切片(动态规划,暴力)

    Leetcode 413. Arithmetic Slice 算术序列切片(动态规划,暴力) 题目描述 如果一个数组1.至少三个元素2.两两之间差值相同,那么这个数组就是算术序列 比如下面的数组都是算 ...

  8. 【Leetcode】413. Arithmetic Slices

    Description A sequence of number is called arithmetic if it consists of at least three elements and ...

  9. 413. Arithmetic Slices

    /**************************Sorry. We do not have enough accepted submissions.*********************** ...

随机推荐

  1. Flume消费内外网分流配置的Kafka时遇到的坑

    网上有铺天盖地的文章,介绍如何将Kafka同时配置成公网地址.内网地址,以实现内外网分流,看着都很成功. 但我们通过Flume消费一个配置了内外网分流的Kafka(版本0.10.1)集群时遇到了坑,却 ...

  2. 使用Docker编译OpenResty支持国密ssl加密

    编译环境 执行编译操作环境如下 #操作系统 CentOS Linux release 7.4.1708 (Core) #docker版本 Version: 19.03.5 编译过程 Dockerfil ...

  3. C/C++ Qt 数据库QSql增删改查组件应用

    Qt SQL模块是Qt中用来操作数据库的类,该类封装了各种SQL数据库接口,可以很方便的链接并使用,数据的获取也使用了典型的Model/View结构,通过MV结构映射我们可以实现数据与通用组件的灵活绑 ...

  4. 前端4 — jQuery — 更新完毕

    1.下载jQuery 网址:Download jQuery | jQuery  最好下载最新版的,因为有什么bug问题,最新版的都会有,所以学技术就用最新版的,实际开发用的时候就要讲究了 2.开始用j ...

  5. 数据集成工具—FlinkX

    @ 目录 FlinkX的安装与简单使用 FlinkX的安装 FlinkX的简单使用 读取mysql中student表中数据 FlinkX本地运行 MySQLToHDFS MySQLToHive MyS ...

  6. 大数据学习day22------spark05------1. 学科最受欢迎老师解法补充 2. 自定义排序 3. spark任务执行过程 4. SparkTask的分类 5. Task的序列化 6. Task的多线程问题

    1. 学科最受欢迎老师解法补充 day21中该案例的解法四还有一个问题,就是当各个老师受欢迎度是一样的时候,其排序规则就处理不了,以下是对其优化的解法 实现方式五 FavoriteTeacher5 p ...

  7. Java实现单链表的增删查改及逆置打印

    //所提供的接口 LinkList.java package Struct; public interface LinkList {//判断链表为空public boolean linkListIsE ...

  8. mysql key与index的区别

    key包含了index, 而index没有key的功能. 1.key 是数据库的物理结构,它包含两层意义,一是约束(偏重于约束和规范数据库的结构完整性),二是索引(辅助查询用的).包括primary ...

  9. awk统计命令(求和、求平均、求最大值、求最小值)

    本节内容:awk统计命令 1.求和 cat data|awk '{sum+=$1} END {print "Sum = ", sum}' 2.求平均 cat data|awk '{ ...

  10. 重量级&轻量级

    重量级 就是说包的大小,还有就是与个人项目的耦合程度,重量级的框架与项目耦合程度大些 代表EJB容器的服务往往是"买一送三",不要都不行 轻量级 就是相对较小的包,当然与项目的耦合 ...