大概有两种思维,一种是面向过程的,就是想象一个数据结构上的操作步骤,推演一下,看看能不能接近一个solution。这种思维有诸多弊端:
1)靠灵光一现的乱试,不可控,不可重复
2)面向过程,比较复杂、麻烦,易出错,且不易记住
3)逻辑性不强,为什么这么操作一番就能得出解? 这个问题很难回答,缺乏一种确信感。离散,不易证明
代数的特点是什么?都是变量和公式推导,从不需要带入离散的具体值去验证,是自证明的,或者说你可以代入任何具体值,都是成立的,逻辑性非常强。比如求斐波那契数列的代数式算法:
```
def f(n):
if n <= 2: return 1;
return f(n - 1) + f(n - 2)
```
就完全是按照斐波那契数列的代数定义coding,你不会想要代入具体的值去验证。
代数思维coding的过程:
1)identify 变量或者说状态,输入参数和返回值肯定是需要考察的变量,可能还有隐含和中间的变量。
2)理清这些变量的关系,关系是一层一层的,就是要拉通题目的解和输入变量以及中间状态之间的关系。
代数思维往往是自顶向下的,分治法,即代数里一个函数由另外的函数和和变量组成
比如 leetcode 413. 等差数列划分,求一个数组所有等差数列的子数组的个数f
1)划分:f=f3 + f4 +... + fn, 原题的解f是以每个元素作为结尾的子数组上的解的和,这是第一层推导
2)针对每个项f(n)
a)如果 当前元素和上一个元素的差等于之前的差:f(n) = f(n - 1) ,f(n-1)对应那些子数组都加一个元素;如果n > 2,可以再多一个,即上一个不满足3个元素,加上当前元素刚好是3个元素的子数组
b) 差不相等:f(n) = 0
```
def numberOfArithmeticSlices(self, nums: List[int]) -> int:
count, cur_count = 0, 1
for i in range(2, len(nums)):
if nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2]:
if i > 2: cur_count += 1
else: cur_count = 0
count += cur_count
return count
```